From d39cdf8ed65b5f75dd4debea52f60575a66f2e0b Mon Sep 17 00:00:00 2001 From: Giovanni Harting <539@idlegandalf.com> Date: Sun, 17 Oct 2021 12:27:35 +0200 Subject: [PATCH] better sharecode api handling, renamed endpoint to /player/id/track --- ent/schema/{RoundStats.go => roundstats.go} | 0 main.go | 101 +++++++++++++++++--- utils/utils.go | 28 +++++- 3 files changed, 114 insertions(+), 15 deletions(-) rename ent/schema/{RoundStats.go => roundstats.go} (100%) diff --git a/ent/schema/RoundStats.go b/ent/schema/roundstats.go similarity index 100% rename from ent/schema/RoundStats.go rename to ent/schema/roundstats.go diff --git a/main.go b/main.go index 3d6bacb..7c73776 100644 --- a/main.go +++ b/main.go @@ -90,8 +90,22 @@ func housekeeping() { for _, tPlayer := range tPlayerNeedShareCodeUpdate { shareCodes, err := utils.GetNewShareCodesForPlayer(tPlayer, db.Lock, conf.Steam.APIKey, rL) if err != nil { - log.Errorf("[HK] Error while request sharecodes: %v", err) - continue + switch err.(type) { + case utils.AuthcodeUnauthorizedError: + db.Lock.Lock() + err = tPlayer.Update().ClearAuthCode().ClearSharecodeUpdated().Exec(context.Background()) + db.Lock.Unlock() + if err != nil { + log.Warningf("[HK] Unable to clear authcode for player %d: %v", tPlayer.ID, err) + } + continue + case utils.SharecodeNoMatchError: + log.Warningf("[HK] last shareCode for player %d does not match player", tPlayer.ID) + continue + default: + log.Errorf("[HK] Error while request sharecodes: %v", err) + continue + } } for _, code := range shareCodes { @@ -256,11 +270,62 @@ func getPlayer(w http.ResponseWriter, r *http.Request) { } } -func postPlayerTrackMe(w http.ResponseWriter, r *http.Request) { +func deletePlayerTrack(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", conf.Httpd.CORSAllowDomains) err := r.ParseForm() if err != nil { - log.Infof("[PPTM] Unable to parse form data: %v", err) + log.Infof("[DPT] Unable to parse form data: %v", err) + w.WriteHeader(http.StatusBadRequest) + return + } + + id := r.Form.Get("id") + authCode := r.Form.Get("authcode") + + if id == "" || authCode == "" || !utils.AuthCodeRegEx.MatchString(authCode) { + log.Infof("[PPTM] invalid arguments: %+v, %+v", id, authCode) + w.WriteHeader(http.StatusBadRequest) + return + } + + tPlayer, err := utils.GetPlayer(db, id, conf.Steam.APIKey, rL) + if err != nil { + log.Infof("[PPT] player not found: %+v", err) + w.WriteHeader(http.StatusNotFound) + return + } + + _, err = utils.IsAuthCodeValid(tPlayer, db.Lock, conf.Steam.APIKey, "", authCode, rL) + if err != nil { + switch e := err.(type) { + case utils.AuthcodeUnauthorizedError: + log.Infof("[DPT] authCode provided for player %s is invalid: %v", id, e) + w.WriteHeader(http.StatusUnauthorized) + return + default: + log.Infof("[DPT] Temporary Steam-API problem: %v", e) + w.WriteHeader(http.StatusServiceUnavailable) + return + } + } + + db.Lock.Lock() + err = tPlayer.Update().ClearAuthCode().ClearSharecodeUpdated().Exec(context.Background()) + db.Lock.Unlock() + if err != nil { + log.Warningf("[PPT] update player failed: %+v", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) +} + +func postPlayerTrack(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", conf.Httpd.CORSAllowDomains) + err := r.ParseForm() + if err != nil { + log.Infof("[PPT] Unable to parse form data: %v", err) w.WriteHeader(http.StatusBadRequest) return } @@ -270,30 +335,41 @@ func postPlayerTrackMe(w http.ResponseWriter, r *http.Request) { shareCode := r.Form.Get("sharecode") if id == "" || authCode == "" || !utils.AuthCodeRegEx.MatchString(authCode) { - log.Infof("[PPTM] invalid arguments: %+v, %+v, %+v", id, authCode, shareCode) + log.Infof("[PPT] invalid arguments: %+v, %+v, %+v", id, authCode, shareCode) w.WriteHeader(http.StatusBadRequest) return } tPlayer, err := utils.GetPlayer(db, id, conf.Steam.APIKey, rL) if err != nil { - log.Infof("[PPTM] player not found: %+v", err) + log.Infof("[PPT] player not found: %+v", err) w.WriteHeader(http.StatusNotFound) return } _, err = utils.IsAuthCodeValid(tPlayer, db.Lock, conf.Steam.APIKey, shareCode, authCode, rL) if err != nil { - log.Infof("[PPTM] authCode provided for player %s is invalid: %v", id, err) - w.WriteHeader(http.StatusUnauthorized) - return + switch e := err.(type) { + case utils.AuthcodeUnauthorizedError: + log.Infof("[PPT] authCode provided for player %s is invalid: %v", id, e) + w.WriteHeader(http.StatusUnauthorized) + return + case utils.SharecodeNoMatchError: + log.Infof("[PPT] shareCode provided for player %s is invalid: %v", id, e) + w.WriteHeader(http.StatusPreconditionFailed) + return + default: + log.Infof("[PPT] Temporary Steam-API problem: %v", e) + w.WriteHeader(http.StatusServiceUnavailable) + return + } } db.Lock.Lock() err = tPlayer.Update().SetAuthCode(authCode).Exec(context.Background()) db.Lock.Unlock() if err != nil { - log.Warningf("[PPTM] update player failed: %+v", err) + log.Warningf("[PPT] update player failed: %+v", err) w.WriteHeader(http.StatusInternalServerError) return } @@ -301,7 +377,7 @@ func postPlayerTrackMe(w http.ResponseWriter, r *http.Request) { if shareCode != "" && utils.ShareCodeRegEx.MatchString(shareCode) { err := demoLoader.LoadDemo(&csgo.Demo{ShareCode: shareCode}) if err != nil { - log.Warningf("[PPTM] unable to queue match: %v", err) + log.Warningf("[PPT] unable to queue match: %v", err) w.WriteHeader(http.StatusServiceUnavailable) return } @@ -611,7 +687,8 @@ func main() { // Define routes router = mux.NewRouter().StrictSlash(true) router.HandleFunc("/player/{id}", getPlayer).Methods(http.MethodGet, http.MethodOptions) - router.HandleFunc("/player/trackme", postPlayerTrackMe).Methods(http.MethodPost, http.MethodOptions) + router.HandleFunc("/player/{id}/track", postPlayerTrack).Methods(http.MethodPost, http.MethodOptions) + router.HandleFunc("/player/{id}/track", deletePlayerTrack).Methods(http.MethodOptions, http.MethodDelete) router.HandleFunc("/match/parse/{sharecode}", getMatchParse).Methods(http.MethodGet, http.MethodOptions) router.HandleFunc("/match/{id:[0-9]{19}}", getMatch).Methods(http.MethodGet, http.MethodOptions) router.HandleFunc("/match/{id:[0-9]{19}}/weapons", getMatchWeapons).Methods(http.MethodGet, http.MethodOptions) diff --git a/utils/utils.go b/utils/utils.go index ffc9032..43cae35 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -8,6 +8,7 @@ import ( "csgowtfd/ent/stats" "encoding/json" "entgo.io/ent/dialect/sql" + "errors" "fmt" "github.com/an0nfunc/go-steamapi" log "github.com/sirupsen/logrus" @@ -187,6 +188,18 @@ type MatchResponse struct { Stats interface{} `json:"stats,omitempty"` } +type ( + AuthcodeUnauthorizedError struct { + error + } + AuthcodeRateLimitError struct { + error + } + SharecodeNoMatchError struct { + error + } +) + const ( shareCodeURLEntry = "https://api.steampowered.com/ICSGOPlayers_730/GetNextMatchSharingCode/v1?key=%s&steamid=%d&steamidkey=%s&knowncode=%s" ) @@ -328,10 +341,19 @@ func getNextShareCode(lastCode string, apiKey string, authCode string, steamId u return "", err } - if r.StatusCode == 202 { + switch r.StatusCode { + case http.StatusAccepted: return "n/a", nil - } else if r.StatusCode != 200 { - return "", fmt.Errorf("bad response from steam api (HTTP %d)", r.StatusCode) + case http.StatusTooManyRequests, http.StatusServiceUnavailable: + return "", AuthcodeRateLimitError{errors.New("api temp. ratelimited")} + case http.StatusPreconditionFailed: + return "", SharecodeNoMatchError{errors.New("sharecode not from player history")} + case http.StatusForbidden: + return "", AuthcodeUnauthorizedError{errors.New("authcode unauthorized")} + case http.StatusOK: + break + default: + return "", errors.New("temporary steamapi error") } defer func(Body io.ReadCloser) {