diff --git a/api/ban.go b/api/ban.go index ea5ee2a..e3deecd 100644 --- a/api/ban.go +++ b/api/ban.go @@ -11,26 +11,32 @@ import ( ) func HandleBan(w http.ResponseWriter, r *http.Request) { - var jsonData map[string]string + var user *database.User + var success bool + var err string var statusCode int switch r.Method { case http.MethodHead: statusCode = http.StatusOK case http.MethodPost: - jsonData, statusCode = handleBanImpl(w, r) + user, success, err, statusCode = handleBanImpl(w, r) default: - jsonData = mmss("error", "Incorrect request. POST or HEAD only.") + err = "Incorrect request. POST or HEAD only." statusCode = http.StatusBadRequest } + if user == nil { + user = &database.User{} + } + w.Header().Set("Content-Type", "application/json") w.Header().Set("Access-Control-Allow-Origin", "*") - if len(jsonData) == 0 { + if r.Method == http.MethodHead { w.WriteHeader(statusCode) } else { - json, _ := json.Marshal(jsonData) + json, _ := json.Marshal(UserActionResponse{*user, success, err}) w.Header().Set("Content-Length", strconv.Itoa(len(json))) w.WriteHeader(statusCode) w.Write(json) @@ -49,30 +55,30 @@ type BanRequestSpec struct { Moderator string } -func handleBanImpl(w http.ResponseWriter, r *http.Request) (map[string]string, int) { +func handleBanImpl(w http.ResponseWriter, r *http.Request) (*database.User, bool, string, int) { // TODO: Actual authentication rather than a fixed secret body, err := io.ReadAll(r.Body) if err != nil { - return mmss("error", "Unable to read request body"), http.StatusBadRequest + return nil, false, "Unable to read request body", http.StatusBadRequest } var req BanRequestSpec err = json.Unmarshal(body, &req) if err != nil { - return mmss("error", err.Error()), http.StatusBadRequest + return nil, false, err.Error(), http.StatusBadRequest } if apiSecret == "" || req.Secret != apiSecret { - return mmss("error", "Invalid API secret in request"), http.StatusUnauthorized + return nil, false, "Invalid API secret in request", http.StatusUnauthorized } if req.Pid == 0 { - return mmss("error", "pid missing or 0 in request"), http.StatusBadRequest + return nil, false, "pid missing or 0 in request", http.StatusBadRequest } if req.Reason == "" { - return mmss("error", "Missing ban reason in request"), http.StatusBadRequest + return nil, false, "Missing ban reason in request", http.StatusBadRequest } moderator := req.Moderator @@ -82,13 +88,13 @@ func handleBanImpl(w http.ResponseWriter, r *http.Request) (map[string]string, i minutes := req.Days*24*60 + req.Hours*60 + req.Minutes if minutes == 0 { - return mmss("error", "Ban length missing or 0"), http.StatusBadRequest + return nil, false, "Ban length missing or 0", http.StatusBadRequest } length := time.Duration(minutes) * time.Minute if !database.BanUser(pool, ctx, req.Pid, req.Tos, length, req.Reason, req.ReasonHidden, moderator) { - return mmss("error", "Failed to ban user"), http.StatusInternalServerError + return nil, false, "Failed to ban user", http.StatusInternalServerError } if req.Tos { @@ -97,6 +103,14 @@ func handleBanImpl(w http.ResponseWriter, r *http.Request) (map[string]string, i gpcm.KickPlayer(req.Pid, "restricted") } - ip := database.GetUserIP(pool, ctx, req.Pid) - return mmss("result", "success", "ip", ip), http.StatusOK + var message string + user, success := database.GetProfile(pool, ctx, req.Pid) + + if success { + message = "" + } else { + message = "Unable to query user data from the database" + } + + return &user, success, message, http.StatusOK } diff --git a/api/kick.go b/api/kick.go index 0090c28..84cdf59 100644 --- a/api/kick.go +++ b/api/kick.go @@ -10,26 +10,32 @@ import ( ) func HandleKick(w http.ResponseWriter, r *http.Request) { - var jsonData map[string]string + var user *database.User + var success bool + var err string var statusCode int switch r.Method { case http.MethodHead: statusCode = http.StatusOK case http.MethodPost: - jsonData, statusCode = handleKickImpl(w, r) + user, success, err, statusCode = handleKickImpl(w, r) default: - jsonData = mmss("error", "Incorrect request. POST or HEAD only.") + err = "Incorrect request. POST or HEAD only." statusCode = http.StatusBadRequest } + if user == nil { + user = &database.User{} + } + w.Header().Set("Content-Type", "application/json") w.Header().Set("Access-Control-Allow-Origin", "*") - if len(jsonData) == 0 { + if r.Method == http.MethodHead { w.WriteHeader(statusCode) } else { - json, _ := json.Marshal(jsonData) + json, _ := json.Marshal(UserActionResponse{*user, success, err}) w.Header().Set("Content-Length", strconv.Itoa(len(json))) w.WriteHeader(statusCode) w.Write(json) @@ -41,30 +47,38 @@ type KickRequestSpec struct { Pid uint32 } -func handleKickImpl(w http.ResponseWriter, r *http.Request) (map[string]string, int) { +func handleKickImpl(w http.ResponseWriter, r *http.Request) (*database.User, bool, string, int) { // TODO: Actual authentication rather than a fixed secret body, err := io.ReadAll(r.Body) if err != nil { - return mmss("error", "Unable to read request body"), http.StatusBadRequest + return nil, false, "Unable to read request body", http.StatusBadRequest } var req KickRequestSpec err = json.Unmarshal(body, &req) if err != nil { - return mmss("error", err.Error()), http.StatusBadRequest + return nil, false, err.Error(), http.StatusBadRequest } if apiSecret == "" || req.Secret != apiSecret { - return mmss("error", "Invalid API secret in request"), http.StatusUnauthorized + return nil, false, "Invalid API secret in request", http.StatusUnauthorized } if req.Pid == 0 { - return mmss("error", "pid missing or 0 in request"), http.StatusBadRequest + return nil, false, "pid missing or 0 in request", http.StatusBadRequest } gpcm.KickPlayer(req.Pid, "moderator_kick") - ip := database.GetUserIP(pool, ctx, req.Pid) - return mmss("status", "success", "ip", ip), http.StatusOK + var message string + user, success := database.GetProfile(pool, ctx, req.Pid) + + if success { + message = "" + } else { + message = "Unable to query user data from the database" + } + + return &user, success, message, http.StatusOK } diff --git a/api/motd.go b/api/motd.go index 53e436b..8170d3c 100644 --- a/api/motd.go +++ b/api/motd.go @@ -9,36 +9,39 @@ import ( ) func HandleMotd(w http.ResponseWriter, r *http.Request) { - var jsonData map[string]string + var motd string + var success bool + var err string var statusCode int switch r.Method { case http.MethodHead: statusCode = http.StatusOK case http.MethodGet: - motd, err := gpcm.GetMessageOfTheDay() - if err != nil { - jsonData = mmss("error", err.Error()) + _motd, motdErr := gpcm.GetMessageOfTheDay() + if motdErr != nil { + err = motdErr.Error() statusCode = http.StatusInternalServerError break } - jsonData = mmss("motd", motd) + motd = _motd + success = true statusCode = http.StatusOK case http.MethodPost: - jsonData, statusCode = handleMotdImpl(w, r) + success, err, statusCode = handleMotdImpl(w, r) default: - jsonData = mmss("error", "Incorrect request. POST, GET, or HEAD only.") + err = "Incorrect request. POST, GET, or HEAD only." statusCode = http.StatusBadRequest } w.Header().Set("Content-Type", "application/json") w.Header().Set("Access-Control-Allow-Origin", "*") - if len(jsonData) == 0 { + if r.Method == http.MethodHead { w.WriteHeader(statusCode) } else { - json, _ := json.Marshal(jsonData) + json, _ := json.Marshal(MotdResponse{motd, success, err}) w.Header().Set("Content-Length", strconv.Itoa(len(json))) w.WriteHeader(statusCode) w.Write(json) @@ -50,29 +53,35 @@ type MotdRequestSpec struct { Motd string } -func handleMotdImpl(w http.ResponseWriter, r *http.Request) (map[string]string, int) { +type MotdResponse struct { + Motd string + Success bool + Error string +} + +func handleMotdImpl(w http.ResponseWriter, r *http.Request) (bool, string, int) { // TODO: Actual authentication rather than a fixed secret body, err := io.ReadAll(r.Body) if err != nil { - return mmss("error", "Unable to read request body"), http.StatusBadRequest + return false, "Unable to read request body", http.StatusBadRequest } var req MotdRequestSpec err = json.Unmarshal(body, &req) if err != nil { - return mmss("error", err.Error()), http.StatusBadRequest + return false, err.Error(), http.StatusBadRequest } if apiSecret == "" || req.Secret != apiSecret { - return mmss("error", "Invalid API secret in request"), http.StatusUnauthorized + return false, "Invalid API secret in request", http.StatusUnauthorized } err = gpcm.SetMessageOfTheDay(req.Motd) if err != nil { - return mmss("error", err.Error()), http.StatusInternalServerError + return false, err.Error(), http.StatusInternalServerError } // Don't return empty JSON, this is placeholder for now. - return mmss("result", "success"), http.StatusOK + return true, "", http.StatusOK } diff --git a/api/unban.go b/api/unban.go index 8d27c41..f91dd25 100644 --- a/api/unban.go +++ b/api/unban.go @@ -9,26 +9,32 @@ import ( ) func HandleUnban(w http.ResponseWriter, r *http.Request) { - var jsonData map[string]string + var user *database.User + var success bool + var err string var statusCode int switch r.Method { case http.MethodHead: statusCode = http.StatusOK case http.MethodPost: - jsonData, statusCode = handleUnbanImpl(w, r) + user, success, err, statusCode = handleUnbanImpl(w, r) default: - jsonData = mmss("error", "Incorrect request. POST or HEAD only.") + err = "Incorrect request. POST or HEAD only." statusCode = http.StatusBadRequest } + if user == nil { + user = &database.User{} + } + w.Header().Set("Content-Type", "application/json") w.Header().Set("Access-Control-Allow-Origin", "*") - if len(jsonData) == 0 { + if r.Method == http.MethodHead { w.WriteHeader(statusCode) } else { - json, _ := json.Marshal(jsonData) + json, _ := json.Marshal(UserActionResponse{*user, success, err}) w.Header().Set("Content-Length", strconv.Itoa(len(json))) w.WriteHeader(statusCode) w.Write(json) @@ -40,32 +46,40 @@ type UnbanRequestSpec struct { Pid uint32 } -func handleUnbanImpl(w http.ResponseWriter, r *http.Request) (map[string]string, int) { +func handleUnbanImpl(w http.ResponseWriter, r *http.Request) (*database.User, bool, string, int) { // TODO: Actual authentication rather than a fixed secret body, err := io.ReadAll(r.Body) if err != nil { - return mmss("error", "Unable to read request body"), http.StatusBadRequest + return nil, false, "Unable to read request body", http.StatusBadRequest } var req UnbanRequestSpec err = json.Unmarshal(body, &req) if err != nil { - return mmss("error", err.Error()), http.StatusBadRequest + return nil, false, err.Error(), http.StatusBadRequest } if apiSecret == "" || req.Secret != apiSecret { - return mmss("error", "Invalid API secret in request"), http.StatusUnauthorized + return nil, false, "Invalid API secret in request", http.StatusUnauthorized } if req.Pid == 0 { - return mmss("error", "pid missing or 0 in request"), http.StatusBadRequest + return nil, false, "pid missing or 0 in request", http.StatusBadRequest } if !database.UnbanUser(pool, ctx, req.Pid) { - return mmss("error", "Failed to unban user"), http.StatusInternalServerError + return nil, false, "Failed to unban user", http.StatusInternalServerError } - ip := database.GetUserIP(pool, ctx, req.Pid) - return mmss("result", "success", "ip", ip), http.StatusOK + var message string + user, success := database.GetProfile(pool, ctx, req.Pid) + + if success { + message = "" + } else { + message = "Unable to query user data from the database" + } + + return &user, success, message, http.StatusOK } diff --git a/api/utils.go b/api/utils.go index 64e03f7..0baa852 100644 --- a/api/utils.go +++ b/api/utils.go @@ -1,18 +1,11 @@ package api -// make map string string -func mmss(data ...string) map[string]string { - ret := make(map[string]string) +import ( + "wwfc/database" +) - l := len(data) - - if l%2 != 0 || l == 0 { - panic("Length of data must be divisible by two") - } - - for i := 0; i < l; i += 2 { - ret[data[i]] = data[i+1] - } - - return ret +type UserActionResponse struct { + User database.User + Success bool + Error string } diff --git a/database/user.go b/database/user.go index 691a772..aba2086 100644 --- a/database/user.go +++ b/database/user.go @@ -15,8 +15,7 @@ const ( UpdateUserTable = `UPDATE users SET firstname = CASE WHEN $3 THEN $2 ELSE firstname END, lastname = CASE WHEN $5 THEN $4 ELSE lastname END, open_host = CASE WHEN $7 THEN $6 ELSE open_host END WHERE profile_id = $1` UpdateUserProfileID = `UPDATE users SET profile_id = $3 WHERE user_id = $1 AND gsbrcd = $2` UpdateUserNGDeviceID = `UPDATE users SET ng_device_id = $2 WHERE profile_id = $1` - GetUser = `SELECT user_id, gsbrcd, email, unique_nick, firstname, lastname, open_host FROM users WHERE profile_id = $1` - GetUserLastIPAddress = `SELECT last_ip_address FROM users WHERE profile_id = $1` + GetUser = `SELECT user_id, gsbrcd, email, unique_nick, firstname, lastname, open_host, last_ip_address, last_ingamesn FROM users WHERE profile_id = $1` DoesUserExist = `SELECT EXISTS(SELECT 1 FROM users WHERE user_id = $1 AND gsbrcd = $2)` IsProfileIDInUse = `SELECT EXISTS(SELECT 1 FROM users WHERE profile_id = $1)` DeleteUserSession = `DELETE FROM sessions WHERE profile_id = $1` @@ -41,6 +40,8 @@ type User struct { Restricted bool RestrictedDeviceId uint32 OpenHost bool + LastInGameSn string + LastIPAddress string } var ( @@ -129,7 +130,7 @@ func (user *User) UpdateProfile(pool *pgxpool.Pool, ctx context.Context, data ma func GetProfile(pool *pgxpool.Pool, ctx context.Context, profileId uint32) (User, bool) { user := User{} row := pool.QueryRow(ctx, GetUser, profileId) - err := row.Scan(&user.UserId, &user.GsbrCode, &user.Email, &user.UniqueNick, &user.FirstName, &user.LastName, &user.OpenHost) + err := row.Scan(&user.UserId, &user.GsbrCode, &user.Email, &user.UniqueNick, &user.FirstName, &user.LastName, &user.OpenHost, &user.LastIPAddress, &user.LastInGameSn) if err != nil { return User{}, false } @@ -148,16 +149,6 @@ func UnbanUser(pool *pgxpool.Pool, ctx context.Context, profileId uint32) bool { return err == nil } -func GetUserIP(pool *pgxpool.Pool, ctx context.Context, profileId uint32) string { - var ip string - err := pool.QueryRow(ctx, GetUserLastIPAddress, profileId).Scan(&ip) - if err != nil { - return "Unknown" - } - - return ip -} - func GetMKWFriendInfo(pool *pgxpool.Pool, ctx context.Context, profileId uint32) string { var info string err := pool.QueryRow(ctx, GetMKWFriendInfoQuery, profileId).Scan(&info)