From b70c693d2625f0ff27d8f5132a14d397f6fd4c1d Mon Sep 17 00:00:00 2001 From: Palapeli <26661008+mkwcat@users.noreply.github.com> Date: Fri, 24 Apr 2026 12:15:57 -0400 Subject: [PATCH] API: Reformat API error responses --- api/ban.go | 6 +++--- api/baninfo.go | 6 +++--- api/groups.go | 1 + api/kick.go | 6 +++++- api/unban.go | 4 ++++ api/util.go | 37 ++++++++++++++++++++++--------------- 6 files changed, 38 insertions(+), 22 deletions(-) diff --git a/api/ban.go b/api/ban.go index 32dfe02..1cbc946 100644 --- a/api/ban.go +++ b/api/ban.go @@ -34,7 +34,7 @@ func HandleBan(w http.ResponseWriter, r *http.Request) { } if req.Reason == "" { - replyError(w, http.StatusBadRequest, APIErrorInvalidBanReason) + replyError(w, http.StatusBadRequest, APIErrorInvalidReason) return } @@ -51,8 +51,6 @@ func HandleBan(w http.ResponseWriter, r *http.Request) { length := time.Duration(minutes) * time.Minute - logging.Notice("API:"+moderator, "Ban profile:", aurora.Cyan(req.ProfileID), "TOS:", aurora.Cyan(req.Tos), "Length:", aurora.Cyan(length), "Reason:", aurora.BrightCyan(req.Reason), "Reason (Hidden):", aurora.BrightCyan(req.ReasonHidden)) - if !db.BanUser(req.ProfileID, req.Tos, length, req.Reason, req.ReasonHidden, moderator) { replyError(w, http.StatusInternalServerError, APIErrorBanFailed) return @@ -70,4 +68,6 @@ func HandleBan(w http.ResponseWriter, r *http.Request) { "reason_hidden": req.ReasonHidden, "moderator": moderator, }) + + logging.Notice("API:"+moderator, "Ban:", aurora.Cyan(req.ProfileID), "TOS:", aurora.Cyan(req.Tos), "Length:", aurora.Cyan(length), "Reason:", aurora.BrightCyan(req.Reason), "Reason (Hidden):", aurora.BrightCyan(req.ReasonHidden)) } diff --git a/api/baninfo.go b/api/baninfo.go index 67444b5..640db7b 100644 --- a/api/baninfo.go +++ b/api/baninfo.go @@ -26,7 +26,7 @@ func HandleBanInfo(w http.ResponseWriter, r *http.Request) { search := query.Get("q") if search == "" { - replyError(w, http.StatusBadRequest, APIErrorInvalidBanQuery) + replyError(w, http.StatusBadRequest, APIErrorInvalidQuery) return } @@ -37,14 +37,14 @@ func HandleBanInfo(w http.ResponseWriter, r *http.Request) { if strings.HasPrefix(search, "NG") { ngId, err := strconv.ParseUint(search[2:], 16, 32) if err != nil { - replyError(w, http.StatusBadRequest, APIErrorInvalidBanQuery) + replyError(w, http.StatusBadRequest, APIErrorInvalidQuery) return } ngDeviceId = uint32(ngId) } else { pId, err := strconv.ParseUint(search, 10, 64) if err != nil { - replyError(w, http.StatusBadRequest, APIErrorInvalidBanQuery) + replyError(w, http.StatusBadRequest, APIErrorInvalidQuery) return } // Truncate to 32 bits as that's how friend codes work diff --git a/api/groups.go b/api/groups.go index 0fea2a7..586cd91 100644 --- a/api/groups.go +++ b/api/groups.go @@ -14,6 +14,7 @@ func HandleGroups(w http.ResponseWriter, r *http.Request) { groups := qr2.GetGroups(query["game"], query["id"], true) if len(groups) == 0 { + // I would return No Content, but here is compatibility replyOK(w, "[]") return } diff --git a/api/kick.go b/api/kick.go index 88aee4b..658a7e6 100644 --- a/api/kick.go +++ b/api/kick.go @@ -4,6 +4,8 @@ import ( "net/http" "wwfc/gpcm" "wwfc/logging" + + "github.com/logrusorgru/aurora/v3" ) type KickRequestSpec struct { @@ -24,7 +26,7 @@ func HandleKick(w http.ResponseWriter, r *http.Request) { } if req.Reason == "" { - replyError(w, http.StatusBadRequest, APIErrorInvalidBanReason) + replyError(w, http.StatusBadRequest, APIErrorInvalidReason) return } @@ -36,4 +38,6 @@ func HandleKick(w http.ResponseWriter, r *http.Request) { "profile_id": req.ProfileID, "reason": req.Reason, }) + + logging.Notice("API:admin", "Kick:", aurora.Cyan(req.ProfileID), "Reason:", aurora.BrightCyan(req.Reason)) } diff --git a/api/unban.go b/api/unban.go index a8df955..ca65073 100644 --- a/api/unban.go +++ b/api/unban.go @@ -3,6 +3,8 @@ package api import ( "net/http" "wwfc/logging" + + "github.com/logrusorgru/aurora/v3" ) type UnbanRequestSpec struct { @@ -31,4 +33,6 @@ func HandleUnban(w http.ResponseWriter, r *http.Request) { logging.Event("profile_unbanned", map[string]any{ "profile_id": req.ProfileID, }) + + logging.Notice("API:admin", "Unban:", aurora.Cyan(req.ProfileID)) } diff --git a/api/util.go b/api/util.go index cd53df1..a8e2300 100644 --- a/api/util.go +++ b/api/util.go @@ -13,15 +13,14 @@ import ( type APIErrorString string const ( - APIErrorAuthenticationFailed APIErrorString = "AuthenticationFailed" - APIErrorInvalidQuery APIErrorString = "InvalidQuery" - APIErrorInvalidProfileID APIErrorString = "InvalidProfileID" - APIErrorInvalidBanReason APIErrorString = "InvalidBanReason" - APIErrorInvalidBanLength APIErrorString = "InvalidBanLength" - APIErrorBanFailed APIErrorString = "BanFailed" - APIErrorUnbanFailed APIErrorString = "UnbanFailed" - APIErrorInvalidBanQuery APIErrorString = "InvalidBanQuery" - APIErrorBanNotFound APIErrorString = "BanNotFound" + APIErrorFailedAuthentication APIErrorString = "failed_authentication" + APIErrorInvalidQuery APIErrorString = "invalid_query" + APIErrorInvalidProfileID APIErrorString = "invalid_profile_id" + APIErrorInvalidReason APIErrorString = "invalid_reason" + APIErrorInvalidBanLength APIErrorString = "invalid_ban_length" + APIErrorBanFailed APIErrorString = "ban_failed" + APIErrorUnbanFailed APIErrorString = "unban_failed" + APIErrorBanNotFound APIErrorString = "ban_not_found" ) type APIError struct { @@ -80,7 +79,7 @@ func parseGet(r *http.Request, w http.ResponseWriter, requiredRole Role) (query authInfo := makeAuthInfo(query) if !authenticate(authInfo, requiredRole) { - replyError(w, http.StatusUnauthorized, APIErrorAuthenticationFailed) + replyError(w, http.StatusUnauthorized, APIErrorFailedAuthentication) return nil, errAuthFailed } return query, nil @@ -127,7 +126,7 @@ func parsePost(r *http.Request, w http.ResponseWriter, parsed any, requiredRole return errNoAuthInfo } if !authenticate(authInfo, requiredRole) { - replyError(w, http.StatusUnauthorized, APIErrorAuthenticationFailed) + replyError(w, http.StatusUnauthorized, APIErrorFailedAuthentication) return errAuthFailed } return nil @@ -159,11 +158,19 @@ func replyOK(w http.ResponseWriter, data any) { } w.Header().Set("Content-Type", "application/json") - jsonData, err := json.Marshal(data) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return + var jsonData []byte + if reflect.ValueOf(data).Kind() == reflect.String { + // Assume it's already JSON + jsonData = []byte(data.(string)) + } else { + var err error + jsonData, err = json.Marshal(data) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } } + w.Header().Set("Content-Length", strconv.Itoa(len(jsonData))) _, _ = w.Write(jsonData) }