From 51610cb3b78e8d0ac8be1ecf04724619a1617d0a Mon Sep 17 00:00:00 2001 From: Palapeli <26661008+mkwcat@users.noreply.github.com> Date: Wed, 22 Apr 2026 16:45:21 -0400 Subject: [PATCH] API: Migrated other endpoints to use new utility functions --- api/groups.go | 27 +++----------------- api/kick.go | 71 ++++++++------------------------------------------- api/stats.go | 32 +++++------------------ api/unban.go | 71 ++++++++------------------------------------------- api/util.go | 1 + 5 files changed, 31 insertions(+), 171 deletions(-) diff --git a/api/groups.go b/api/groups.go index d4d9eef..0fea2a7 100644 --- a/api/groups.go +++ b/api/groups.go @@ -1,41 +1,22 @@ package api import ( - "encoding/json" "net/http" - "net/url" - "strconv" "wwfc/qr2" ) func HandleGroups(w http.ResponseWriter, r *http.Request) { - u, err := url.Parse(r.URL.String()) + query, err := parseGet(r, w, RoleNone) if err != nil { - w.WriteHeader(http.StatusBadRequest) - return - } - - query, err := url.ParseQuery(u.RawQuery) - if err != nil { - w.WriteHeader(http.StatusBadRequest) return } groups := qr2.GetGroups(query["game"], query["id"], true) - var jsonData []byte if len(groups) == 0 { - jsonData, _ = json.Marshal([]string{}) - } else { - jsonData, err = json.Marshal(groups) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } + replyOK(w, "[]") + return } - w.Header().Set("Content-Type", "application/json") - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Content-Length", strconv.Itoa(len(jsonData))) - _, _ = w.Write(jsonData) + replyOK(w, groups) } diff --git a/api/kick.go b/api/kick.go index f90e6b5..88aee4b 100644 --- a/api/kick.go +++ b/api/kick.go @@ -1,90 +1,39 @@ package api import ( - "encoding/json" - "io" "net/http" - "strconv" "wwfc/gpcm" "wwfc/logging" ) -func HandleKick(w http.ResponseWriter, r *http.Request) { - var success bool - var err string - var statusCode int - - switch r.Method { - case http.MethodPost: - success, err, statusCode = handleKickImpl(r) - case http.MethodOptions: - statusCode = http.StatusNoContent - w.Header().Set("Access-Control-Allow-Methods", "POST") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type") - default: - err = "Incorrect request. POST only." - statusCode = http.StatusMethodNotAllowed - w.Header().Set("Allow", "POST") - } - - w.Header().Set("Access-Control-Allow-Origin", "*") - - var jsonData []byte - - if statusCode != http.StatusNoContent { - w.Header().Set("Content-Type", "application/json") - - if success { - jsonData, _ = json.Marshal(map[string]string{"success": "true"}) - } else { - jsonData, _ = json.Marshal(map[string]string{"error": err}) - } - } - - w.Header().Set("Content-Length", strconv.Itoa(len(jsonData))) - - w.WriteHeader(statusCode) - _, _ = w.Write(jsonData) -} - type KickRequestSpec struct { - Secret string `json:"secret"` + AuthInfo Reason string `json:"reason"` ProfileID uint32 `json:"pid"` } -func handleKickImpl(r *http.Request) (bool, string, int) { - // TODO: Actual authentication rather than a fixed secret - - body, err := io.ReadAll(r.Body) - if err != nil { - return false, "Unable to read request body", http.StatusBadRequest - } - +func HandleKick(w http.ResponseWriter, r *http.Request) { var req KickRequestSpec - err = json.Unmarshal(body, &req) - if err != nil { - return false, err.Error(), http.StatusBadRequest - } - - if apiSecret == "" || req.Secret != apiSecret { - return false, "Invalid API secret in request", http.StatusUnauthorized + if err := parsePost(r, w, &req, RoleModerator); err != nil { + return } if req.ProfileID == 0 { - return false, "Profile ID missing or 0 in request", http.StatusBadRequest + replyError(w, http.StatusBadRequest, APIErrorInvalidProfileID) + return } if req.Reason == "" { - return false, "Missing kick reason in request", http.StatusBadRequest + replyError(w, http.StatusBadRequest, APIErrorInvalidBanReason) + return } + replyOK(w, nil) + gpcm.KickPlayerCustomMessage(req.ProfileID, req.Reason, gpcm.WWFCMsgKickedCustom) logging.Event("profile_kicked", map[string]any{ "profile_id": req.ProfileID, "reason": req.Reason, }) - - return true, "", http.StatusOK } diff --git a/api/stats.go b/api/stats.go index e35c743..5e1a9a5 100644 --- a/api/stats.go +++ b/api/stats.go @@ -1,41 +1,31 @@ package api import ( - "encoding/json" "net/http" - "net/url" - "strconv" "wwfc/common" "wwfc/qr2" ) -type Stats struct { +type StatsResponseSpec struct { OnlinePlayerCount int `json:"online"` ActivePlayerCount int `json:"active"` GroupCount int `json:"groups"` } func HandleStats(w http.ResponseWriter, r *http.Request) { - u, err := url.Parse(r.URL.String()) + query, err := parseGet(r, w, RoleNone) if err != nil { - w.WriteHeader(http.StatusBadRequest) - return - } - - query, err := url.ParseQuery(u.RawQuery) - if err != nil { - w.WriteHeader(http.StatusBadRequest) return } games := query["game"] - stats := map[string]Stats{} + stats := map[string]StatsResponseSpec{} servers := qr2.GetSessionServers() groups := qr2.GetGroups([]string{}, []string{}, false) - globalStats := Stats{ + globalStats := StatsResponseSpec{ OnlinePlayerCount: len(servers), ActivePlayerCount: 0, GroupCount: len(groups), @@ -54,7 +44,7 @@ func HandleStats(w http.ResponseWriter, r *http.Request) { gameStats, exists := stats[gameName] if !exists { - gameStats = Stats{ + gameStats = StatsResponseSpec{ OnlinePlayerCount: 0, ActivePlayerCount: 0, GroupCount: 0, @@ -76,15 +66,5 @@ func HandleStats(w http.ResponseWriter, r *http.Request) { } stats["global"] = globalStats - - jsonData, err := json.Marshal(stats) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Content-Length", strconv.Itoa(len(jsonData))) - _, _ = w.Write(jsonData) + replyOK(w, stats) } diff --git a/api/unban.go b/api/unban.go index 7e49af1..a8df955 100644 --- a/api/unban.go +++ b/api/unban.go @@ -1,85 +1,34 @@ package api import ( - "encoding/json" - "io" "net/http" - "strconv" "wwfc/logging" ) -func HandleUnban(w http.ResponseWriter, r *http.Request) { - var success bool - var err string - var statusCode int - - switch r.Method { - case http.MethodPost: - success, err, statusCode = handleUnbanImpl(r) - case http.MethodOptions: - statusCode = http.StatusNoContent - w.Header().Set("Access-Control-Allow-Methods", "POST") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type") - default: - err = "Incorrect request. POST only." - statusCode = http.StatusMethodNotAllowed - w.Header().Set("Allow", "POST") - } - - w.Header().Set("Access-Control-Allow-Origin", "*") - - var jsonData []byte - - if statusCode != http.StatusNoContent { - w.Header().Set("Content-Type", "application/json") - - if success { - jsonData, _ = json.Marshal(map[string]string{"success": "true"}) - } else { - jsonData, _ = json.Marshal(map[string]string{"error": err}) - } - } - - w.Header().Set("Content-Length", strconv.Itoa(len(jsonData))) - - w.WriteHeader(statusCode) - _, _ = w.Write(jsonData) -} - type UnbanRequestSpec struct { - Secret string `json:"secret"` + AuthInfo ProfileID uint32 `json:"pid"` } -func handleUnbanImpl(r *http.Request) (bool, string, int) { - // TODO: Actual authentication rather than a fixed secret - - body, err := io.ReadAll(r.Body) - if err != nil { - return false, "Unable to read request body", http.StatusBadRequest - } - +func HandleUnban(w http.ResponseWriter, r *http.Request) { var req UnbanRequestSpec - err = json.Unmarshal(body, &req) - if err != nil { - return false, err.Error(), http.StatusBadRequest - } - - if apiSecret == "" || req.Secret != apiSecret { - return false, "Invalid API secret in request", http.StatusUnauthorized + if err := parsePost(r, w, &req, RoleModerator); err != nil { + return } if req.ProfileID == 0 { - return false, "pid missing or 0 in request", http.StatusBadRequest + replyError(w, http.StatusBadRequest, APIErrorInvalidProfileID) + return } if !db.UnbanUser(req.ProfileID) { - return false, "Failed to unban user", http.StatusInternalServerError + replyError(w, http.StatusInternalServerError, APIErrorUnbanFailed) + return } + replyOK(w, nil) + logging.Event("profile_unbanned", map[string]any{ "profile_id": req.ProfileID, }) - - return true, "", http.StatusOK } diff --git a/api/util.go b/api/util.go index 7148f32..cd53df1 100644 --- a/api/util.go +++ b/api/util.go @@ -19,6 +19,7 @@ const ( APIErrorInvalidBanReason APIErrorString = "InvalidBanReason" APIErrorInvalidBanLength APIErrorString = "InvalidBanLength" APIErrorBanFailed APIErrorString = "BanFailed" + APIErrorUnbanFailed APIErrorString = "UnbanFailed" APIErrorInvalidBanQuery APIErrorString = "InvalidBanQuery" APIErrorBanNotFound APIErrorString = "BanNotFound" )