aboutsummaryrefslogtreecommitdiff
path: root/pkg/api
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/api')
-rw-r--r--pkg/api/api0/accounts.go109
-rw-r--r--pkg/api/api0/api.go17
-rw-r--r--pkg/api/api0/client.go174
-rw-r--r--pkg/api/api0/errors.go36
-rw-r--r--pkg/api/api0/playerinfo.go34
-rw-r--r--pkg/api/api0/server.go132
6 files changed, 137 insertions, 365 deletions
diff --git a/pkg/api/api0/accounts.go b/pkg/api/api0/accounts.go
index 76e47b6..e4bc085 100644
--- a/pkg/api/api0/accounts.go
+++ b/pkg/api/api0/accounts.go
@@ -29,31 +29,19 @@ func (h *Handler) handleAccountsWritePersistence(w http.ResponseWriter, r *http.
}
if err := r.ParseMultipartForm(2 << 20); err != nil {
- respJSON(w, r, http.StatusNotFound, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("failed to parse multipart form: %v", err),
- })
+ respFail(w, r, http.StatusNotFound, ErrorCode_BAD_REQUEST.MessageObjf("failed to parse multipart form: %v", err))
return
}
pf, pfHdr, err := r.FormFile("pdata")
if err != nil {
- respJSON(w, r, http.StatusNotFound, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("missing pdata file: %v", err),
- })
+ respFail(w, r, http.StatusNotFound, ErrorCode_BAD_REQUEST.MessageObjf("missing pdata file: %v", err))
return
}
defer pf.Close()
if pfHdr.Size > (2 << 20) {
- respJSON(w, r, http.StatusNotFound, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("pdata file is too large"),
- })
+ respFail(w, r, http.StatusNotFound, ErrorCode_BAD_REQUEST.MessageObjf("pdata file is too large"))
return
}
@@ -62,11 +50,7 @@ func (h *Handler) handleAccountsWritePersistence(w http.ResponseWriter, r *http.
hlog.FromRequest(r).Error().
Err(err).
Msgf("failed to read uploaded data file (size: %d)", pfHdr.Size)
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Message(),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
}
@@ -75,11 +59,7 @@ func (h *Handler) handleAccountsWritePersistence(w http.ResponseWriter, r *http.
hlog.FromRequest(r).Warn().
Err(err).
Msgf("invalid pdata rejected")
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("invalid pdata"),
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_BAD_REQUEST.MessageObjf("invalid pdata"))
return
}
@@ -87,30 +67,19 @@ func (h *Handler) handleAccountsWritePersistence(w http.ResponseWriter, r *http.
hlog.FromRequest(r).Warn().
Err(err).
Msgf("pdata with too much trailing junk rejected")
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("invalid pdata"),
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_BAD_REQUEST.MessageObjf("invalid pdata"))
return
}
uidQ := r.URL.Query().Get("id")
if uidQ == "" {
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("id param is required"),
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_BAD_REQUEST.MessageObjf("id param is required"))
return
}
uid, err := strconv.ParseUint(uidQ, 10, 64)
if err != nil {
- respJSON(w, r, http.StatusNotFound, map[string]any{
- "success": false,
- "error": ErrorCode_PLAYER_NOT_FOUND,
- })
+ respFail(w, r, http.StatusNotFound, ErrorCode_PLAYER_NOT_FOUND.MessageObj())
return
}
@@ -121,11 +90,7 @@ func (h *Handler) handleAccountsWritePersistence(w http.ResponseWriter, r *http.
hlog.FromRequest(r).Error().
Err(err).
Msgf("failed to parse remote ip %q", r.RemoteAddr)
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Message(),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
}
@@ -135,51 +100,31 @@ func (h *Handler) handleAccountsWritePersistence(w http.ResponseWriter, r *http.
Err(err).
Uint64("uid", uid).
Msgf("failed to read account from storage")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Message(),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
}
if acct == nil {
- respJSON(w, r, http.StatusNotFound, map[string]any{
- "success": false,
- "error": ErrorCode_PLAYER_NOT_FOUND,
- })
+ respFail(w, r, http.StatusNotFound, ErrorCode_PLAYER_NOT_FOUND.MessageObj())
return
}
if acct.IsOnOwnServer() {
if acct.AuthIP != raddr.Addr() {
- respJSON(w, r, http.StatusForbidden, map[string]any{
- "success": false,
- "error": ErrorCode_UNAUTHORIZED_GAMESERVER,
- })
+ respFail(w, r, http.StatusForbidden, ErrorCode_UNAUTHORIZED_GAMESERVER.MessageObj())
return
}
} else {
srv := h.ServerList.GetServerByID(serverID)
if srv == nil {
- respJSON(w, r, http.StatusForbidden, map[string]any{
- "success": false,
- "error": ErrorCode_UNAUTHORIZED_GAMESERVER,
- "msg": ErrorCode_UNAUTHORIZED_GAMESERVER.Messagef("no such game server"),
- })
+ respFail(w, r, http.StatusForbidden, ErrorCode_UNAUTHORIZED_GAMESERVER.MessageObjf("no such game server"))
return
}
if srv.Addr.Addr() != raddr.Addr() {
- respJSON(w, r, http.StatusForbidden, map[string]any{
- "success": false,
- "error": ErrorCode_UNAUTHORIZED_GAMESERVER,
- })
+ respFail(w, r, http.StatusForbidden, ErrorCode_UNAUTHORIZED_GAMESERVER.MessageObj())
return
}
if acct.LastServerID != srv.ID {
- respJSON(w, r, http.StatusForbidden, map[string]any{
- "success": false,
- "error": ErrorCode_UNAUTHORIZED_GAMESERVER,
- })
+ respFail(w, r, http.StatusForbidden, ErrorCode_UNAUTHORIZED_GAMESERVER.MessageObj())
return
}
}
@@ -189,11 +134,7 @@ func (h *Handler) handleAccountsWritePersistence(w http.ResponseWriter, r *http.
Err(err).
Uint64("uid", uid).
Msgf("failed to save pdata")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Message(),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
}
@@ -223,8 +164,7 @@ func (h *Handler) handleAccountsLookupUID(w http.ResponseWriter, r *http.Request
"success": false,
"username": "",
"matches": []uint64{},
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("username param is required"),
+ "error": ErrorCode_BAD_REQUEST.MessageObjf("username param is required"),
})
return
}
@@ -243,8 +183,7 @@ func (h *Handler) handleAccountsLookupUID(w http.ResponseWriter, r *http.Request
"success": false,
"username": username,
"matches": []uint64{},
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Message(),
+ "error": ErrorCode_INTERNAL_SERVER_ERROR.MessageObj(),
})
return
}
@@ -279,8 +218,7 @@ func (h *Handler) handleAccountsGetUsername(w http.ResponseWriter, r *http.Reque
"success": false,
"uid": "",
"matches": []string{},
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("uid param is required"),
+ "error": ErrorCode_BAD_REQUEST.MessageObjf("uid param is required"),
})
return
}
@@ -291,7 +229,7 @@ func (h *Handler) handleAccountsGetUsername(w http.ResponseWriter, r *http.Reque
"success": false,
"uid": strconv.FormatUint(uid, 10),
"matches": []string{},
- "error": ErrorCode_PLAYER_NOT_FOUND,
+ "error": ErrorCode_PLAYER_NOT_FOUND.MessageObj(),
})
return
}
@@ -304,8 +242,9 @@ func (h *Handler) handleAccountsGetUsername(w http.ResponseWriter, r *http.Reque
Msgf("failed to read account from storage")
respJSON(w, r, http.StatusInternalServerError, map[string]any{
"success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Message(),
+ "uid": strconv.FormatUint(uid, 10),
+ "matches": []string{},
+ "error": ErrorCode_INTERNAL_SERVER_ERROR.MessageObj(),
})
return
}
@@ -314,7 +253,7 @@ func (h *Handler) handleAccountsGetUsername(w http.ResponseWriter, r *http.Reque
"success": false,
"uid": strconv.FormatUint(uid, 10),
"matches": []string{},
- "error": ErrorCode_PLAYER_NOT_FOUND,
+ "error": ErrorCode_PLAYER_NOT_FOUND.MessageObj(),
})
return
}
diff --git a/pkg/api/api0/api.go b/pkg/api/api0/api.go
index 818f6b9..398ac17 100644
--- a/pkg/api/api0/api.go
+++ b/pkg/api/api0/api.go
@@ -151,6 +151,23 @@ func (h *Handler) checkLauncherVersion(r *http.Request) bool {
return semver.Compare(rver, mver) >= 0
}
+// respFail writes a {success:false,error:ErrorObj} response with the provided
+// response status.
+func respFail(w http.ResponseWriter, r *http.Request, status int, obj ErrorObj) {
+ if rid, ok := hlog.IDFromRequest(r); ok {
+ respJSON(w, r, status, map[string]any{
+ "success": false,
+ "error": obj,
+ "request_id": rid.String(),
+ })
+ } else {
+ respJSON(w, r, status, map[string]any{
+ "success": false,
+ "error": obj,
+ })
+ }
+}
+
// respJSON writes the JSON encoding of obj with the provided response status.
func respJSON(w http.ResponseWriter, r *http.Request, status int, obj any) {
if r.Method == http.MethodHead {
diff --git a/pkg/api/api0/client.go b/pkg/api/api0/client.go
index 7964a95..d5cd659 100644
--- a/pkg/api/api0/client.go
+++ b/pkg/api/api0/client.go
@@ -84,29 +84,19 @@ func (h *Handler) handleClientOriginAuth(w http.ResponseWriter, r *http.Request)
}
if !h.checkLauncherVersion(r) {
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_UNSUPPORTED_VERSION,
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_UNSUPPORTED_VERSION.MessageObj())
return
}
uidQ := r.URL.Query().Get("id")
if uidQ == "" {
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("id param is required"),
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_BAD_REQUEST.MessageObjf("id param is required"))
return
}
uid, err := strconv.ParseUint(uidQ, 10, 64)
if err != nil {
- respJSON(w, r, http.StatusNotFound, map[string]any{
- "success": false,
- "error": ErrorCode_PLAYER_NOT_FOUND,
- })
+ respFail(w, r, http.StatusNotFound, ErrorCode_PLAYER_NOT_FOUND.MessageObj())
return
}
@@ -115,22 +105,14 @@ func (h *Handler) handleClientOriginAuth(w http.ResponseWriter, r *http.Request)
hlog.FromRequest(r).Error().
Err(err).
Msgf("failed to parse remote ip %q", r.RemoteAddr)
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Message(),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
}
if !h.InsecureDevNoCheckPlayerAuth {
token := r.URL.Query().Get("token")
if token == "" {
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("token param is required"),
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_BAD_REQUEST.MessageObjf("token param is required"))
return
}
@@ -151,11 +133,7 @@ func (h *Handler) handleClientOriginAuth(w http.ResponseWriter, r *http.Request)
Str("stryder_token", string(token)).
Str("stryder_resp", string(stryderRes)).
Msgf("invalid stryder token")
- respJSON(w, r, http.StatusForbidden, map[string]any{
- "success": false,
- "error": ErrorCode_UNAUTHORIZED_GAME,
- "msg": ErrorCode_UNAUTHORIZED_GAME.Message(),
- })
+ respFail(w, r, http.StatusForbidden, ErrorCode_UNAUTHORIZED_GAME.MessageObj())
return
case errors.Is(err, stryder.ErrStryder):
hlog.FromRequest(r).Error().
@@ -164,11 +142,7 @@ func (h *Handler) handleClientOriginAuth(w http.ResponseWriter, r *http.Request)
Str("stryder_token", string(token)).
Str("stryder_resp", string(stryderRes)).
Msgf("unexpected stryder error")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Message(),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
default:
hlog.FromRequest(r).Error().
@@ -177,11 +151,7 @@ func (h *Handler) handleClientOriginAuth(w http.ResponseWriter, r *http.Request)
Str("stryder_token", string(token)).
Str("stryder_resp", string(stryderRes)).
Msgf("unexpected stryder error")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Messagef("stryder is down: %v", err),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObjf("stryder is down: %v", err))
return
}
}
@@ -261,11 +231,7 @@ func (h *Handler) handleClientOriginAuth(w http.ResponseWriter, r *http.Request)
hlog.FromRequest(r).Error().
Err(err).
Msgf("failed to generate random token")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Message(),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
} else {
acct.AuthToken = t
@@ -282,11 +248,7 @@ func (h *Handler) handleClientOriginAuth(w http.ResponseWriter, r *http.Request)
Err(err).
Uint64("uid", uid).
Msgf("failed to save account to storage")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Message(),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
}
@@ -313,29 +275,19 @@ func (h *Handler) handleClientAuthWithServer(w http.ResponseWriter, r *http.Requ
}
if !h.checkLauncherVersion(r) {
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_UNSUPPORTED_VERSION,
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_UNSUPPORTED_VERSION.MessageObj())
return
}
uidQ := r.URL.Query().Get("id")
if uidQ == "" {
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("id param is required"),
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_BAD_REQUEST.MessageObjf("id param is required"))
return
}
uid, err := strconv.ParseUint(uidQ, 10, 64)
if err != nil {
- respJSON(w, r, http.StatusNotFound, map[string]any{
- "success": false,
- "error": ErrorCode_PLAYER_NOT_FOUND,
- })
+ respFail(w, r, http.StatusNotFound, ErrorCode_PLAYER_NOT_FOUND.MessageObj())
return
}
@@ -345,10 +297,7 @@ func (h *Handler) handleClientAuthWithServer(w http.ResponseWriter, r *http.Requ
srv := h.ServerList.GetServerByID(server)
if srv == nil || srv.Password != password {
- respJSON(w, r, http.StatusUnauthorized, map[string]any{
- "success": false,
- "error": ErrorCode_UNAUTHORIZED_PWD,
- })
+ respFail(w, r, http.StatusUnauthorized, ErrorCode_UNAUTHORIZED_PWD.MessageObj())
return
}
@@ -358,26 +307,17 @@ func (h *Handler) handleClientAuthWithServer(w http.ResponseWriter, r *http.Requ
Err(err).
Uint64("uid", uid).
Msgf("failed to read account from storage")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
}
if acct == nil {
- respJSON(w, r, http.StatusNotFound, map[string]any{
- "success": false,
- "error": ErrorCode_PLAYER_NOT_FOUND,
- })
+ respFail(w, r, http.StatusNotFound, ErrorCode_PLAYER_NOT_FOUND.MessageObj())
return
}
if !h.InsecureDevNoCheckPlayerAuth {
if playerToken != acct.AuthToken || !time.Now().Before(acct.AuthTokenExpiry) {
- respJSON(w, r, http.StatusUnauthorized, map[string]any{
- "success": false,
- "error": ErrorCode_INVALID_MASTERSERVER_TOKEN,
- })
+ respFail(w, r, http.StatusUnauthorized, ErrorCode_INVALID_MASTERSERVER_TOKEN.MessageObj())
return
}
}
@@ -387,10 +327,7 @@ func (h *Handler) handleClientAuthWithServer(w http.ResponseWriter, r *http.Requ
hlog.FromRequest(r).Error().
Err(err).
Msgf("failed to generate random token")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
} else {
authToken = v
@@ -402,10 +339,7 @@ func (h *Handler) handleClientAuthWithServer(w http.ResponseWriter, r *http.Requ
Err(err).
Uint64("uid", acct.UID).
Msgf("failed to read pdata from storage")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
} else if !exists {
pbuf = pdata.DefaultPdata
@@ -423,26 +357,17 @@ func (h *Handler) handleClientAuthWithServer(w http.ResponseWriter, r *http.Requ
}
switch {
case errors.Is(err, api0gameserver.ErrAuthFailed):
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_JSON_PARSE_ERROR, // this is kind of misleading... but it's what the original master server did
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_JSON_PARSE_ERROR.MessageObj()) // this is kind of misleading... but it's what the original master server did
case errors.Is(err, api0gameserver.ErrInvalidResponse):
hlog.FromRequest(r).Error().
Err(err).
Msgf("failed to make gameserver auth request")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_GAMESERVER_RESPONSE,
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_BAD_GAMESERVER_RESPONSE.MessageObj())
default:
hlog.FromRequest(r).Error().
Err(err).
Msgf("failed to make gameserver auth request")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
}
return
}
@@ -455,10 +380,7 @@ func (h *Handler) handleClientAuthWithServer(w http.ResponseWriter, r *http.Requ
Err(err).
Uint64("uid", uid).
Msgf("failed to save account to storage")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
}
@@ -487,29 +409,19 @@ func (h *Handler) handleClientAuthWithSelf(w http.ResponseWriter, r *http.Reques
}
if !h.checkLauncherVersion(r) {
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_UNSUPPORTED_VERSION,
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_UNSUPPORTED_VERSION.MessageObj())
return
}
uidQ := r.URL.Query().Get("id")
if uidQ == "" {
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("id param is required"),
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_BAD_REQUEST.MessageObjf("id param is required"))
return
}
uid, err := strconv.ParseUint(uidQ, 10, 64)
if err != nil {
- respJSON(w, r, http.StatusNotFound, map[string]any{
- "success": false,
- "error": ErrorCode_PLAYER_NOT_FOUND,
- })
+ respFail(w, r, http.StatusNotFound, ErrorCode_PLAYER_NOT_FOUND.MessageObj())
return
}
@@ -521,27 +433,17 @@ func (h *Handler) handleClientAuthWithSelf(w http.ResponseWriter, r *http.Reques
Err(err).
Uint64("uid", uid).
Msgf("failed to read account from storage")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Message(),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
}
if acct == nil {
- respJSON(w, r, http.StatusNotFound, map[string]any{
- "success": false,
- "error": ErrorCode_PLAYER_NOT_FOUND,
- })
+ respFail(w, r, http.StatusNotFound, ErrorCode_PLAYER_NOT_FOUND.MessageObj())
return
}
if !h.InsecureDevNoCheckPlayerAuth {
if playerToken != acct.AuthToken || !time.Now().Before(acct.AuthTokenExpiry) {
- respJSON(w, r, http.StatusUnauthorized, map[string]any{
- "success": false,
- "error": ErrorCode_INVALID_MASTERSERVER_TOKEN,
- })
+ respFail(w, r, http.StatusUnauthorized, ErrorCode_INVALID_MASTERSERVER_TOKEN.MessageObj())
return
}
}
@@ -553,11 +455,7 @@ func (h *Handler) handleClientAuthWithSelf(w http.ResponseWriter, r *http.Reques
Err(err).
Uint64("uid", uid).
Msgf("failed to save account to storage")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Message(),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
}
@@ -572,11 +470,7 @@ func (h *Handler) handleClientAuthWithSelf(w http.ResponseWriter, r *http.Reques
Err(err).
Uint64("uid", acct.UID).
Msgf("failed to read pdata from storage")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Message(),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
} else if !exists {
obj["persistentData"] = marshalJSONBytesAsArray(pdata.DefaultPdata)
@@ -590,11 +484,7 @@ func (h *Handler) handleClientAuthWithSelf(w http.ResponseWriter, r *http.Reques
hlog.FromRequest(r).Error().
Err(err).
Msgf("failed to generate random token")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Message(),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
} else {
obj["authToken"] = v
diff --git a/pkg/api/api0/errors.go b/pkg/api/api0/errors.go
index dc12d42..e407be6 100644
--- a/pkg/api/api0/errors.go
+++ b/pkg/api/api0/errors.go
@@ -1,6 +1,8 @@
package api0
-import "fmt"
+import (
+ "fmt"
+)
// ErrorCode represents a known Northstar error code.
type ErrorCode string
@@ -26,6 +28,35 @@ const (
ErrorCode_BAD_REQUEST ErrorCode = "BAD_REQUEST"
)
+// ErrorObj contains an error code and a message for API responses.
+type ErrorObj struct {
+ Code ErrorCode `json:"enum"`
+ Message string `json:"msg"` // note: no omitempty
+}
+
+// Obj returns an ErrorObj.
+func (n ErrorCode) Obj() ErrorObj {
+ return ErrorObj{
+ Code: n,
+ }
+}
+
+// MessageObj is like Message, but returns an ErrorObj.
+func (n ErrorCode) MessageObj() ErrorObj {
+ return ErrorObj{
+ Code: n,
+ Message: n.Message(),
+ }
+}
+
+// MessageObjf is like Messagef, but returns an ErrorObj.
+func (n ErrorCode) MessageObjf(format string, a ...interface{}) ErrorObj {
+ return ErrorObj{
+ Code: n,
+ Message: n.Messagef(format, a...),
+ }
+}
+
// Message returns the default message for error code n.
func (n ErrorCode) Message() string {
switch n {
@@ -64,5 +95,8 @@ func (n ErrorCode) Message() string {
// Messagef returns Message() with additional text appended after ": ".
func (n ErrorCode) Messagef(format string, a ...interface{}) string {
+ if format == "" {
+ return n.Message()
+ }
return n.Message() + ": " + fmt.Sprintf(format, a...)
}
diff --git a/pkg/api/api0/playerinfo.go b/pkg/api/api0/playerinfo.go
index ca059cd..70555cc 100644
--- a/pkg/api/api0/playerinfo.go
+++ b/pkg/api/api0/playerinfo.go
@@ -80,20 +80,13 @@ func (h *Handler) handlePlayer(w http.ResponseWriter, r *http.Request) {
uidQ := r.URL.Query().Get("id")
if uidQ == "" {
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("id param is required"),
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_BAD_REQUEST.MessageObjf("id param is required"))
return
}
uid, err := strconv.ParseUint(uidQ, 10, 64)
if err != nil {
- respJSON(w, r, http.StatusNotFound, map[string]any{
- "success": false,
- "error": ErrorCode_PLAYER_NOT_FOUND,
- })
+ respFail(w, r, http.StatusNotFound, ErrorCode_PLAYER_NOT_FOUND.MessageObj())
return
}
@@ -123,18 +116,11 @@ func (h *Handler) handlePlayer(w http.ResponseWriter, r *http.Request) {
Err(err).
Uint64("uid", uid).
Msgf("failed to read pdata from storage")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Message(),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
}
if !exists {
- respJSON(w, r, http.StatusNotFound, map[string]any{
- "success": false,
- "error": ErrorCode_PLAYER_NOT_FOUND,
- })
+ respFail(w, r, http.StatusNotFound, ErrorCode_PLAYER_NOT_FOUND.MessageObj())
return
}
@@ -148,11 +134,7 @@ func (h *Handler) handlePlayer(w http.ResponseWriter, r *http.Request) {
Uint64("uid", uid).
Str("pdata_sha256", hex.EncodeToString(hash[:])).
Msgf("failed to parse pdata from storage")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Messagef("failed to parse pdata from storage"),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObjf("failed to parse stored pdata"))
return
}
@@ -163,11 +145,7 @@ func (h *Handler) handlePlayer(w http.ResponseWriter, r *http.Request) {
Uint64("uid", uid).
Str("pdata_sha256", hex.EncodeToString(hash[:])).
Msgf("failed to encode pdata as json")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Messagef("failed to encode pdata as json"),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObjf("failed to encode pdata as json"))
return
}
jbuf = append(jbuf, '\n')
diff --git a/pkg/api/api0/server.go b/pkg/api/api0/server.go
index 5536bcb..756c48b 100644
--- a/pkg/api/api0/server.go
+++ b/pkg/api/api0/server.go
@@ -51,10 +51,7 @@ func (h *Handler) handleServerUpsert(w http.ResponseWriter, r *http.Request) {
}
if !h.checkLauncherVersion(r) {
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_UNSUPPORTED_VERSION,
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_UNSUPPORTED_VERSION.MessageObj())
return
}
@@ -63,21 +60,13 @@ func (h *Handler) handleServerUpsert(w http.ResponseWriter, r *http.Request) {
hlog.FromRequest(r).Error().
Err(err).
Msgf("failed to parse remote ip %q", r.RemoteAddr)
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Message(),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
}
if !h.AllowGameServerIPv6 {
if raddr.Addr().Is6() {
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_NO_GAMESERVER_RESPONSE,
- "msg": ErrorCode_NO_GAMESERVER_RESPONSE.Messagef("ipv6 is not currently supported (ip %s)", raddr.Addr()),
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_BAD_REQUEST.MessageObjf("ipv6 is not currently supported (ip %s)", raddr.Addr()))
return
}
}
@@ -110,11 +99,7 @@ func (h *Handler) handleServerUpsert(w http.ResponseWriter, r *http.Request) {
if canUpdate {
if v := r.URL.Query().Get("id"); v == "" {
if isUpdate {
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("id param is required"),
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_BAD_REQUEST.MessageObjf("port param is required"))
return
}
} else {
@@ -125,19 +110,11 @@ func (h *Handler) handleServerUpsert(w http.ResponseWriter, r *http.Request) {
if canCreate {
if v := r.URL.Query().Get("port"); v == "" {
if isCreate {
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("port param is required"),
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_BAD_REQUEST.MessageObjf("port param is required"))
return
}
} else if n, err := strconv.ParseUint(v, 10, 16); err != nil {
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("port param is invalid: %v", err),
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_BAD_REQUEST.MessageObjf("port param is invalid: %v", err))
return
} else {
s.Addr = netip.AddrPortFrom(raddr.Addr(), uint16(n))
@@ -145,19 +122,11 @@ func (h *Handler) handleServerUpsert(w http.ResponseWriter, r *http.Request) {
if v := r.URL.Query().Get("authPort"); v == "" {
if isCreate {
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("authPort param is required"),
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_BAD_REQUEST.MessageObjf("authPort param is required"))
+ return
}
- return
} else if n, err := strconv.ParseUint(v, 10, 16); err != nil {
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("authPort param is invalid: %v", err),
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_BAD_REQUEST.MessageObjf("authPort param is invalid: %v", err))
return
} else {
s.AuthPort = uint16(n)
@@ -165,11 +134,7 @@ func (h *Handler) handleServerUpsert(w http.ResponseWriter, r *http.Request) {
if v := r.URL.Query().Get("password"); len(v) > 128 {
if isCreate {
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("password is too long"),
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_BAD_REQUEST.MessageObjf("password is too long"))
return
}
} else {
@@ -180,11 +145,7 @@ func (h *Handler) handleServerUpsert(w http.ResponseWriter, r *http.Request) {
if canCreate || canUpdate {
if v := r.URL.Query().Get("name"); v == "" {
if isCreate {
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("name param must not be empty"),
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_BAD_REQUEST.MessageObjf("name param must not be empty"))
return
}
} else {
@@ -312,45 +273,25 @@ func (h *Handler) handleServerUpsert(w http.ResponseWriter, r *http.Request) {
nsrv, err := h.ServerList.ServerHybridUpdatePut(u, s, l)
if err != nil {
if errors.Is(err, ErrServerListUpdateWrongIP) {
- respJSON(w, r, http.StatusForbidden, map[string]any{
- "success": false,
- "error": ErrorCode_UNAUTHORIZED_GAMESERVER,
- "msg": ErrorCode_UNAUTHORIZED_GAMESERVER.Messagef("%v", err),
- })
+ respFail(w, r, http.StatusForbidden, ErrorCode_UNAUTHORIZED_GAMESERVER.MessageObjf("%v", err))
return
}
if errors.Is(err, ErrServerListUpdateServerDead) {
- respJSON(w, r, http.StatusForbidden, map[string]any{
- "success": false,
- "error": ErrorCode_UNAUTHORIZED_GAMESERVER,
- "msg": ErrorCode_UNAUTHORIZED_GAMESERVER.Messagef("no such server"),
- })
+ respFail(w, r, http.StatusForbidden, ErrorCode_UNAUTHORIZED_GAMESERVER.MessageObjf("no such server"))
return
}
if errors.Is(err, ErrServerListDuplicateAuthAddr) {
- respJSON(w, r, http.StatusForbidden, map[string]any{
- "success": false,
- "error": ErrorCode_DUPLICATE_SERVER,
- "msg": ErrorCode_DUPLICATE_SERVER.Messagef("%v", err),
- })
+ respFail(w, r, http.StatusForbidden, ErrorCode_DUPLICATE_SERVER.MessageObjf("%v", err))
return
}
if errors.Is(err, ErrServerListLimitExceeded) {
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Messagef("%v", err),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObjf("%v", err))
return
}
hlog.FromRequest(r).Error().
Err(err).
Msgf("failed to update server list")
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Message(),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
}
@@ -368,29 +309,17 @@ func (h *Handler) handleServerUpsert(w http.ResponseWriter, r *http.Request) {
} else {
code = ErrorCode_NO_GAMESERVER_RESPONSE
}
- respJSON(w, r, http.StatusBadGateway, map[string]any{
- "success": false,
- "error": code,
- "msg": code.Messagef("failed to connect to auth port: %v", err),
- })
+ respFail(w, r, http.StatusBadGateway, code.MessageObjf("failed to connect to auth port: %v", err))
return
}
if err := a2s.Probe(s.Addr, time.Until(nsrv.VerificationDeadline)); err != nil {
- respJSON(w, r, http.StatusBadGateway, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_GAMESERVER_RESPONSE,
- "msg": ErrorCode_BAD_GAMESERVER_RESPONSE.Messagef("failed to connect to game port: %v", err),
- })
+ respFail(w, r, http.StatusBadGateway, ErrorCode_BAD_GAMESERVER_RESPONSE.MessageObjf("failed to connect to game port: %v", err))
return
}
if !h.ServerList.VerifyServer(nsrv.ID) {
- respJSON(w, r, http.StatusBadGateway, map[string]any{
- "success": false,
- "error": ErrorCode_NO_GAMESERVER_RESPONSE,
- "msg": ErrorCode_NO_GAMESERVER_RESPONSE.Messagef("verification timed out"),
- })
+ respFail(w, r, http.StatusBadGateway, ErrorCode_NO_GAMESERVER_RESPONSE.MessageObjf("verification timed out"))
return
}
}
@@ -423,21 +352,13 @@ func (h *Handler) handleServerRemove(w http.ResponseWriter, r *http.Request) {
hlog.FromRequest(r).Error().
Err(err).
Msgf("failed to parse remote ip %q", r.RemoteAddr)
- respJSON(w, r, http.StatusInternalServerError, map[string]any{
- "success": false,
- "error": ErrorCode_INTERNAL_SERVER_ERROR,
- "msg": ErrorCode_INTERNAL_SERVER_ERROR.Message(),
- })
+ respFail(w, r, http.StatusInternalServerError, ErrorCode_INTERNAL_SERVER_ERROR.MessageObj())
return
}
var id string
if v := r.URL.Query().Get("id"); v == "" {
- respJSON(w, r, http.StatusBadRequest, map[string]any{
- "success": false,
- "error": ErrorCode_BAD_REQUEST,
- "msg": ErrorCode_BAD_REQUEST.Messagef("id param is required"),
- })
+ respFail(w, r, http.StatusBadRequest, ErrorCode_BAD_REQUEST.MessageObjf("id param is required"))
return
} else {
id = v
@@ -445,18 +366,11 @@ func (h *Handler) handleServerRemove(w http.ResponseWriter, r *http.Request) {
srv := h.ServerList.GetServerByID(id)
if srv == nil {
- respJSON(w, r, http.StatusForbidden, map[string]any{
- "success": false,
- "error": ErrorCode_UNAUTHORIZED_GAMESERVER,
- "msg": ErrorCode_UNAUTHORIZED_GAMESERVER.Messagef("no such game server"),
- })
+ respFail(w, r, http.StatusForbidden, ErrorCode_UNAUTHORIZED_GAMESERVER.MessageObjf("no such game server"))
return
}
if srv.Addr.Addr() != raddr.Addr() {
- respJSON(w, r, http.StatusForbidden, map[string]any{
- "success": false,
- "error": ErrorCode_UNAUTHORIZED_GAMESERVER,
- })
+ respFail(w, r, http.StatusForbidden, ErrorCode_UNAUTHORIZED_GAMESERVER.MessageObj())
return
}
h.ServerList.DeleteServerByID(id)