Completely change response structure
This commit is contained in:
parent
14d926e31d
commit
7387ed4295
|
@ -10,13 +10,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Method wraps an API method to a HandlerFunc.
|
// Method wraps an API method to a HandlerFunc.
|
||||||
func Method(f func(md common.MethodData) common.Response, db *sql.DB, privilegesNeeded ...int) gin.HandlerFunc {
|
func Method(f func(md common.MethodData) common.CodeMessager, db *sql.DB, privilegesNeeded ...int) gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
initialCaretaker(c, f, db, privilegesNeeded...)
|
initialCaretaker(c, f, db, privilegesNeeded...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func initialCaretaker(c *gin.Context, f func(md common.MethodData) common.Response, db *sql.DB, privilegesNeeded ...int) {
|
func initialCaretaker(c *gin.Context, f func(md common.MethodData) common.CodeMessager, db *sql.DB, privilegesNeeded ...int) {
|
||||||
data, err := ioutil.ReadAll(c.Request.Body)
|
data, err := ioutil.ReadAll(c.Request.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Error(err)
|
c.Error(err)
|
||||||
|
@ -52,22 +52,25 @@ func initialCaretaker(c *gin.Context, f func(md common.MethodData) common.Respon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if missingPrivileges != 0 {
|
if missingPrivileges != 0 {
|
||||||
c.IndentedJSON(401, common.Response{
|
c.IndentedJSON(401, common.SimpleResponse(401, "You don't have the privilege(s): "+common.Privileges(missingPrivileges).String()+"."))
|
||||||
Code: 401,
|
|
||||||
Message: "You don't have the privilege(s): " + common.Privileges(missingPrivileges).String() + ".",
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := f(md)
|
resp := f(md)
|
||||||
if resp.Code == 0 {
|
if resp.GetCode() == 0 {
|
||||||
resp.Code = 500
|
// Dirty hack to set the code
|
||||||
|
type setCoder interface {
|
||||||
|
SetCode(int)
|
||||||
|
}
|
||||||
|
if newver, can := resp.(setCoder); can {
|
||||||
|
newver.SetCode(500)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, exists := c.GetQuery("pls200"); exists {
|
if _, exists := c.GetQuery("pls200"); exists {
|
||||||
c.Writer.WriteHeader(200)
|
c.Writer.WriteHeader(200)
|
||||||
} else {
|
} else {
|
||||||
c.Writer.WriteHeader(resp.Code)
|
c.Writer.WriteHeader(resp.GetCode())
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, exists := c.GetQuery("callback"); exists {
|
if _, exists := c.GetQuery("callback"); exists {
|
||||||
|
|
|
@ -5,11 +5,18 @@ import (
|
||||||
"github.com/osuripple/api/common"
|
"github.com/osuripple/api/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type response404 struct {
|
||||||
|
common.ResponseBase
|
||||||
|
Cats string `json:"cats"`
|
||||||
|
}
|
||||||
|
|
||||||
// Handle404 handles requests with no implemented handlers.
|
// Handle404 handles requests with no implemented handlers.
|
||||||
func Handle404(c *gin.Context) {
|
func Handle404(c *gin.Context) {
|
||||||
c.IndentedJSON(404, common.Response{
|
c.IndentedJSON(404, response404{
|
||||||
Code: 404,
|
ResponseBase: common.ResponseBase{
|
||||||
Message: "Oh dear... that API request could not be found! Perhaps the API is not up-to-date? Either way, have a surprise!",
|
Code: 404,
|
||||||
Data: surpriseMe(),
|
Message: "Oh dear... that API request could not be found! Perhaps the API is not up-to-date? Either way, have a surprise!",
|
||||||
|
},
|
||||||
|
Cats: surpriseMe(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,53 +6,57 @@ import (
|
||||||
"github.com/osuripple/api/common"
|
"github.com/osuripple/api/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
type badgeData struct {
|
type singleBadge struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Icon string `json:"icon"`
|
Icon string `json:"icon"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type badgeData struct {
|
||||||
|
common.ResponseBase
|
||||||
|
singleBadge
|
||||||
|
}
|
||||||
|
|
||||||
// BadgeByIDGET is the handler for /badge/:id
|
// BadgeByIDGET is the handler for /badge/:id
|
||||||
func BadgeByIDGET(md common.MethodData) (r common.Response) {
|
func BadgeByIDGET(md common.MethodData) common.CodeMessager {
|
||||||
b := badgeData{}
|
var b badgeData
|
||||||
err := md.DB.QueryRow("SELECT id, name, icon FROM badges WHERE id=? LIMIT 1", md.C.Param("id")).Scan(&b.ID, &b.Name, &b.Icon)
|
err := md.DB.QueryRow("SELECT id, name, icon FROM badges WHERE id=? LIMIT 1", md.C.Param("id")).Scan(&b.ID, &b.Name, &b.Icon)
|
||||||
switch {
|
switch {
|
||||||
case err == sql.ErrNoRows:
|
case err == sql.ErrNoRows:
|
||||||
r.Code = 404
|
return common.SimpleResponse(404, "No such badge was found")
|
||||||
r.Message = "No such badge was found"
|
|
||||||
return
|
|
||||||
case err != nil:
|
case err != nil:
|
||||||
md.Err(err)
|
md.Err(err)
|
||||||
r = Err500
|
return Err500
|
||||||
return
|
|
||||||
}
|
}
|
||||||
r.Code = 200
|
b.Code = 200
|
||||||
r.Data = b
|
return b
|
||||||
return
|
}
|
||||||
|
|
||||||
|
type multiBadgeData struct {
|
||||||
|
common.ResponseBase
|
||||||
|
Badges []singleBadge `json:"badges"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// BadgesGET retrieves all the badges on this ripple instance.
|
// BadgesGET retrieves all the badges on this ripple instance.
|
||||||
func BadgesGET(md common.MethodData) (r common.Response) {
|
func BadgesGET(md common.MethodData) common.CodeMessager {
|
||||||
var badges []badgeData
|
var r multiBadgeData
|
||||||
rows, err := md.DB.Query("SELECT id, name, icon FROM badges")
|
rows, err := md.DB.Query("SELECT id, name, icon FROM badges")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
md.Err(err)
|
md.Err(err)
|
||||||
r = Err500
|
return Err500
|
||||||
return
|
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
nb := badgeData{}
|
nb := singleBadge{}
|
||||||
err = rows.Scan(&nb.ID, &nb.Name, &nb.Icon)
|
err = rows.Scan(&nb.ID, &nb.Name, &nb.Icon)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
md.Err(err)
|
md.Err(err)
|
||||||
}
|
}
|
||||||
badges = append(badges, nb)
|
r.Badges = append(r.Badges, nb)
|
||||||
}
|
}
|
||||||
if err := rows.Err(); err != nil {
|
if err := rows.Err(); err != nil {
|
||||||
md.Err(err)
|
md.Err(err)
|
||||||
}
|
}
|
||||||
r.Code = 200
|
r.ResponseBase.Code = 200
|
||||||
r.Data = badges
|
return r
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,19 +8,13 @@ import (
|
||||||
|
|
||||||
// Boilerplate errors
|
// Boilerplate errors
|
||||||
var (
|
var (
|
||||||
Err500 = common.Response{
|
Err500 = common.SimpleResponse(500, "An error occurred. Trying again may work. If it doesn't, yell at this Ripple instance admin and tell them to fix the API.")
|
||||||
Code: 500,
|
ErrBadJSON = common.SimpleResponse(400, "Your JSON for this request is invalid.")
|
||||||
Message: "An error occurred. Try again, perhaps?",
|
|
||||||
}
|
|
||||||
ErrBadJSON = common.Response{
|
|
||||||
Code: 400,
|
|
||||||
Message: "There was an error processing your JSON data.",
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrMissingField generates a response to a request when some fields in the JSON are missing.
|
// ErrMissingField generates a response to a request when some fields in the JSON are missing.
|
||||||
func ErrMissingField(missingFields ...string) common.Response {
|
func ErrMissingField(missingFields ...string) common.CodeMessager {
|
||||||
return common.Response{
|
return common.ResponseBase{
|
||||||
Code: 422, // http://stackoverflow.com/a/10323055/5328069
|
Code: 422, // http://stackoverflow.com/a/10323055/5328069
|
||||||
Message: "Missing fields: " + strings.Join(missingFields, ", ") + ".",
|
Message: "Missing fields: " + strings.Join(missingFields, ", ") + ".",
|
||||||
}
|
}
|
||||||
|
|
101
app/v1/friend.go
101
app/v1/friend.go
|
@ -13,15 +13,19 @@ type friendData struct {
|
||||||
IsMutual bool `json:"is_mutual"`
|
IsMutual bool `json:"is_mutual"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type friendsGETResponse struct {
|
||||||
|
common.ResponseBase
|
||||||
|
Friends []friendData `json:"friends"`
|
||||||
|
}
|
||||||
|
|
||||||
// FriendsGET is the API request handler for GET /friends.
|
// FriendsGET is the API request handler for GET /friends.
|
||||||
// It retrieves an user's friends, and whether the friendship is mutual or not.
|
// It retrieves an user's friends, and whether the friendship is mutual or not.
|
||||||
func FriendsGET(md common.MethodData) (r common.Response) {
|
func FriendsGET(md common.MethodData) common.CodeMessager {
|
||||||
var myFrienders []int
|
var myFrienders []int
|
||||||
myFriendersRaw, err := md.DB.Query("SELECT user1 FROM users_relationships WHERE user2 = ?", md.ID())
|
myFriendersRaw, err := md.DB.Query("SELECT user1 FROM users_relationships WHERE user2 = ?", md.ID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
md.Err(err)
|
md.Err(err)
|
||||||
r = Err500
|
return Err500
|
||||||
return
|
|
||||||
}
|
}
|
||||||
defer myFriendersRaw.Close()
|
defer myFriendersRaw.Close()
|
||||||
for myFriendersRaw.Next() {
|
for myFriendersRaw.Next() {
|
||||||
|
@ -55,8 +59,7 @@ ORDER BY users_relationships.id`
|
||||||
results, err := md.DB.Query(myFriendsQuery+common.Paginate(md.C.Query("p"), md.C.Query("l")), md.ID())
|
results, err := md.DB.Query(myFriendsQuery+common.Paginate(md.C.Query("p"), md.C.Query("l")), md.ID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
md.Err(err)
|
md.Err(err)
|
||||||
r = Err500
|
return Err500
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var myFriends []friendData
|
var myFriends []friendData
|
||||||
|
@ -76,9 +79,10 @@ ORDER BY users_relationships.id`
|
||||||
md.Err(err)
|
md.Err(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r := friendsGETResponse{}
|
||||||
r.Code = 200
|
r.Code = 200
|
||||||
r.Data = myFriends
|
r.Friends = myFriends
|
||||||
return
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func friendPuts(md common.MethodData, row *sql.Rows) (user friendData) {
|
func friendPuts(md common.MethodData, row *sql.Rows) (user friendData) {
|
||||||
|
@ -104,39 +108,37 @@ func friendPuts(md common.MethodData, row *sql.Rows) (user friendData) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type friendsWithData struct {
|
type friendsWithResponse struct {
|
||||||
|
common.ResponseBase
|
||||||
Friends bool `json:"friend"`
|
Friends bool `json:"friend"`
|
||||||
Mutual bool `json:"mutual"`
|
Mutual bool `json:"mutual"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FriendsWithGET checks the current user is friends with the one passed in the request path.
|
// FriendsWithGET checks the current user is friends with the one passed in the request path.
|
||||||
func FriendsWithGET(md common.MethodData) (r common.Response) {
|
func FriendsWithGET(md common.MethodData) common.CodeMessager {
|
||||||
|
r := friendsWithResponse{}
|
||||||
r.Code = 200
|
r.Code = 200
|
||||||
var d friendsWithData
|
|
||||||
uid, err := strconv.Atoi(md.C.Param("id"))
|
uid, err := strconv.Atoi(md.C.Param("id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Data = d
|
return common.SimpleResponse(400, "That is not a number!")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
err = md.DB.QueryRow("SELECT EXISTS(SELECT 1 FROM users_relationships WHERE user1 = ? AND user2 = ? LIMIT 1), EXISTS(SELECT 1 FROM users_relationships WHERE user2 = ? AND user1 = ? LIMIT 1)", md.ID(), uid, md.ID(), uid).Scan(&d.Friends, &d.Mutual)
|
err = md.DB.QueryRow("SELECT EXISTS(SELECT 1 FROM users_relationships WHERE user1 = ? AND user2 = ? LIMIT 1), EXISTS(SELECT 1 FROM users_relationships WHERE user2 = ? AND user1 = ? LIMIT 1)", md.ID(), uid, md.ID(), uid).Scan(&r.Friends, &r.Mutual)
|
||||||
if err != sql.ErrNoRows && err != nil {
|
if err != sql.ErrNoRows && err != nil {
|
||||||
md.Err(err)
|
md.Err(err)
|
||||||
r = Err500
|
return Err500
|
||||||
return
|
|
||||||
}
|
}
|
||||||
r.Data = d
|
if !r.Friends {
|
||||||
return
|
r.Mutual = false
|
||||||
|
}
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// FriendsAddGET is the GET version of FriendsAddPOST.
|
// FriendsAddGET is the GET version of FriendsAddPOST.
|
||||||
func FriendsAddGET(md common.MethodData) common.Response {
|
func FriendsAddGET(md common.MethodData) common.CodeMessager {
|
||||||
uidS := md.C.Param("id")
|
uidS := md.C.Param("id")
|
||||||
uid, err := strconv.Atoi(uidS)
|
uid, err := strconv.Atoi(uidS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Response{
|
return common.SimpleResponse(400, "Nope. That's not a number.")
|
||||||
Code: 400,
|
|
||||||
Message: "Nope. That's not a number.",
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return addFriend(md, uid)
|
return addFriend(md, uid)
|
||||||
}
|
}
|
||||||
|
@ -146,26 +148,21 @@ type friendAddPOSTData struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FriendsAddPOST allows for adding friends. Yup. Easy as that.
|
// FriendsAddPOST allows for adding friends. Yup. Easy as that.
|
||||||
func FriendsAddPOST(md common.MethodData) (r common.Response) {
|
func FriendsAddPOST(md common.MethodData) common.CodeMessager {
|
||||||
d := friendAddPOSTData{}
|
d := friendAddPOSTData{}
|
||||||
err := md.RequestData.Unmarshal(&d)
|
err := md.RequestData.Unmarshal(&d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r = ErrBadJSON
|
return ErrBadJSON
|
||||||
return
|
|
||||||
}
|
}
|
||||||
return addFriend(md, d.UserID)
|
return addFriend(md, d.UserID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addFriend(md common.MethodData, u int) (r common.Response) {
|
func addFriend(md common.MethodData, u int) common.CodeMessager {
|
||||||
if md.ID() == u {
|
if md.ID() == u {
|
||||||
r.Code = 400
|
return common.SimpleResponse(406, "Just so you know: you can't add yourself to your friends.")
|
||||||
r.Message = "Just so you know: you can't add yourself to your friends."
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if !userExists(md, u) {
|
if !userExists(md, u) {
|
||||||
r.Code = 404
|
return common.SimpleResponse(404, "I'd also like to be friends with someone who doesn't even exist (???), however that's NOT POSSIBLE.")
|
||||||
r.Message = "I'd also like to be friends with someone who doesn't even exist (???), however that's NOT POSSIBLE."
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
relExists bool
|
relExists bool
|
||||||
|
@ -174,23 +171,20 @@ func addFriend(md common.MethodData, u int) (r common.Response) {
|
||||||
err := md.DB.QueryRow("SELECT EXISTS(SELECT 1 FROM users_relationships WHERE user1 = ? AND user2 = ?), EXISTS(SELECT 1 FROM users_relationships WHERE user2 = ? AND user1 = ?)", md.ID(), u, md.ID(), u).Scan(&relExists, &isMutual)
|
err := md.DB.QueryRow("SELECT EXISTS(SELECT 1 FROM users_relationships WHERE user1 = ? AND user2 = ?), EXISTS(SELECT 1 FROM users_relationships WHERE user2 = ? AND user1 = ?)", md.ID(), u, md.ID(), u).Scan(&relExists, &isMutual)
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
md.Err(err)
|
md.Err(err)
|
||||||
r = Err500
|
return Err500
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if !relExists {
|
if !relExists {
|
||||||
_, err := md.DB.Exec("INSERT INTO users_relationships(user1, user2) VALUES (?, ?)", md.User.UserID, u)
|
_, err := md.DB.Exec("INSERT INTO users_relationships(user1, user2) VALUES (?, ?)", md.User.UserID, u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
md.Err(err)
|
md.Err(err)
|
||||||
r = Err500
|
return Err500
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var r friendsWithResponse
|
||||||
r.Code = 200
|
r.Code = 200
|
||||||
r.Data = friendsWithData{
|
r.Friends = true
|
||||||
Friends: true,
|
r.Mutual = isMutual
|
||||||
Mutual: isMutual,
|
return r
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// userExists makes sure an user exists.
|
// userExists makes sure an user exists.
|
||||||
|
@ -203,40 +197,35 @@ func userExists(md common.MethodData, u int) (r bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FriendsDelGET is the GET version of FriendDelPOST.
|
// FriendsDelGET is the GET version of FriendDelPOST.
|
||||||
func FriendsDelGET(md common.MethodData) common.Response {
|
func FriendsDelGET(md common.MethodData) common.CodeMessager {
|
||||||
uidS := md.C.Param("id")
|
uidS := md.C.Param("id")
|
||||||
uid, err := strconv.Atoi(uidS)
|
uid, err := strconv.Atoi(uidS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Response{
|
return common.SimpleResponse(400, "Nope. That's not a number.")
|
||||||
Code: 400,
|
|
||||||
Message: "Nope. That's not a number.",
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return delFriend(md, uid)
|
return delFriend(md, uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FriendsDelPOST allows for deleting friends.
|
// FriendsDelPOST allows for deleting friends.
|
||||||
func FriendsDelPOST(md common.MethodData) (r common.Response) {
|
func FriendsDelPOST(md common.MethodData) common.CodeMessager {
|
||||||
d := friendAddPOSTData{}
|
d := friendAddPOSTData{}
|
||||||
err := md.RequestData.Unmarshal(&d)
|
err := md.RequestData.Unmarshal(&d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r = ErrBadJSON
|
return ErrBadJSON
|
||||||
return
|
|
||||||
}
|
}
|
||||||
return delFriend(md, d.UserID)
|
return delFriend(md, d.UserID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func delFriend(md common.MethodData, u int) common.Response {
|
func delFriend(md common.MethodData, u int) common.CodeMessager {
|
||||||
_, err := md.DB.Exec("DELETE FROM users_relationships WHERE user1 = ? AND user2 = ?", md.ID(), u)
|
_, err := md.DB.Exec("DELETE FROM users_relationships WHERE user1 = ? AND user2 = ?", md.ID(), u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
md.Err(err)
|
md.Err(err)
|
||||||
return Err500
|
return Err500
|
||||||
}
|
}
|
||||||
return common.Response{
|
r := friendsWithResponse{
|
||||||
Code: 200,
|
Friends: false,
|
||||||
Data: friendsWithData{
|
Mutual: false,
|
||||||
Friends: false,
|
|
||||||
Mutual: false,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
r.Code = 200
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,22 +8,18 @@ type setAllowedData struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserManageSetAllowedPOST allows to set the allowed status of an user.
|
// UserManageSetAllowedPOST allows to set the allowed status of an user.
|
||||||
func UserManageSetAllowedPOST(md common.MethodData) (r common.Response) {
|
func UserManageSetAllowedPOST(md common.MethodData) common.CodeMessager {
|
||||||
data := setAllowedData{}
|
data := setAllowedData{}
|
||||||
if err := md.RequestData.Unmarshal(&data); err != nil {
|
if err := md.RequestData.Unmarshal(&data); err != nil {
|
||||||
r = ErrBadJSON
|
return ErrBadJSON
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if data.Allowed < 0 || data.Allowed > 2 {
|
if data.Allowed < 0 || data.Allowed > 2 {
|
||||||
r.Code = 400
|
return common.SimpleResponse(400, "Allowed status must be between 0 and 2")
|
||||||
r.Message = "Allowed status must be between 0 and 2"
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
_, err := md.DB.Exec("UPDATE users SET allowed = ? WHERE id = ?", data.Allowed, data.UserID)
|
_, err := md.DB.Exec("UPDATE users SET allowed = ? WHERE id = ?", data.Allowed, data.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
md.Err(err)
|
md.Err(err)
|
||||||
r = Err500
|
return Err500
|
||||||
return
|
|
||||||
}
|
}
|
||||||
query := `
|
query := `
|
||||||
SELECT users.id, users.username, register_datetime, rank,
|
SELECT users.id, users.username, register_datetime, rank,
|
||||||
|
@ -34,6 +30,5 @@ LEFT JOIN users_stats
|
||||||
ON users.id=users_stats.id
|
ON users.id=users_stats.id
|
||||||
WHERE users.id=?
|
WHERE users.id=?
|
||||||
LIMIT 1`
|
LIMIT 1`
|
||||||
r = userPuts(md, md.DB.QueryRow(query, data.UserID))
|
return userPuts(md, md.DB.QueryRow(query, data.UserID))
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,60 +14,59 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// MetaRestartGET restarts the API with Zero Downtime™.
|
// MetaRestartGET restarts the API with Zero Downtime™.
|
||||||
func MetaRestartGET(md common.MethodData) (r common.Response) {
|
func MetaRestartGET(md common.MethodData) common.CodeMessager {
|
||||||
proc, err := os.FindProcess(syscall.Getpid())
|
proc, err := os.FindProcess(syscall.Getpid())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Code = 500
|
return common.SimpleResponse(500, "couldn't find process. what the fuck?")
|
||||||
r.Message = "couldn't find process. what the fuck?"
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
r.Code = 200
|
|
||||||
r.Message = "brb"
|
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
proc.Signal(syscall.SIGUSR2)
|
proc.Signal(syscall.SIGUSR2)
|
||||||
}()
|
}()
|
||||||
return
|
return common.SimpleResponse(200, "brb")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MetaKillGET kills the API process. NOTE TO EVERYONE: NEVER. EVER. USE IN PROD.
|
// MetaKillGET kills the API process. NOTE TO EVERYONE: NEVER. EVER. USE IN PROD.
|
||||||
// Mainly created because I couldn't bother to fire up a terminal, do htop and kill the API each time.
|
// Mainly created because I couldn't bother to fire up a terminal, do htop and kill the API each time.
|
||||||
func MetaKillGET(md common.MethodData) (r common.Response) {
|
func MetaKillGET(md common.MethodData) common.CodeMessager {
|
||||||
proc, err := os.FindProcess(syscall.Getpid())
|
proc, err := os.FindProcess(syscall.Getpid())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Code = 500
|
return common.SimpleResponse(500, "couldn't find process. what the fuck?")
|
||||||
r.Message = "couldn't find process. what the fuck?"
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
const form = "02/01/2006"
|
const form = "02/01/2006"
|
||||||
r.Code = 200
|
r := common.ResponseBase{
|
||||||
|
Code: 200,
|
||||||
|
Message: fmt.Sprintf("RIP ripple API %s - %s", upSince.Format(form), time.Now().Format(form)),
|
||||||
|
}
|
||||||
// yes
|
// yes
|
||||||
r.Message = fmt.Sprintf("RIP ripple API %s - %s", upSince.Format(form), time.Now().Format(form))
|
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
proc.Kill()
|
proc.Kill()
|
||||||
}()
|
}()
|
||||||
return
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
var upSince = time.Now()
|
var upSince = time.Now()
|
||||||
|
|
||||||
|
type metaUpSinceResponse struct {
|
||||||
|
common.ResponseBase
|
||||||
|
Code int `json:"code"`
|
||||||
|
Since int64 `json:"since"`
|
||||||
|
}
|
||||||
|
|
||||||
// MetaUpSinceGET retrieves the moment the API application was started.
|
// MetaUpSinceGET retrieves the moment the API application was started.
|
||||||
// Mainly used to get if the API was restarted.
|
// Mainly used to get if the API was restarted.
|
||||||
func MetaUpSinceGET(md common.MethodData) common.Response {
|
func MetaUpSinceGET(md common.MethodData) common.CodeMessager {
|
||||||
return common.Response{
|
return metaUpSinceResponse{
|
||||||
Code: 200,
|
Code: 200,
|
||||||
Data: upSince.UnixNano(),
|
Since: int64(upSince.UnixNano()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MetaUpdateGET updates the API to the latest version, and restarts it.
|
// MetaUpdateGET updates the API to the latest version, and restarts it.
|
||||||
func MetaUpdateGET(md common.MethodData) common.Response {
|
func MetaUpdateGET(md common.MethodData) common.CodeMessager {
|
||||||
if f, err := os.Stat(".git"); err == os.ErrNotExist || !f.IsDir() {
|
if f, err := os.Stat(".git"); err == os.ErrNotExist || !f.IsDir() {
|
||||||
return common.Response{
|
return common.SimpleResponse(500, "instance is not using git")
|
||||||
Code: 500,
|
|
||||||
Message: "repo is not using git",
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
// go get
|
// go get
|
||||||
|
@ -87,10 +86,7 @@ func MetaUpdateGET(md common.MethodData) common.Response {
|
||||||
}
|
}
|
||||||
proc.Signal(syscall.SIGUSR2)
|
proc.Signal(syscall.SIGUSR2)
|
||||||
}()
|
}()
|
||||||
return common.Response{
|
return common.SimpleResponse(200, "Started updating! "+surpriseMe())
|
||||||
Code: 200,
|
|
||||||
Message: "Started updating! " + surpriseMe(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func execCommand(command string, args ...string) bool {
|
func execCommand(command string, args ...string) bool {
|
||||||
|
|
|
@ -75,42 +75,52 @@ var randomSentences = [...]string{
|
||||||
"sudo rm -rf /",
|
"sudo rm -rf /",
|
||||||
"Hi! I'm Flowey! Flowey the flower!",
|
"Hi! I'm Flowey! Flowey the flower!",
|
||||||
"Ripple devs are actually cats",
|
"Ripple devs are actually cats",
|
||||||
"Support Howl's fund for buying a power supply for his SSD",
|
"Support Howl's fund for buying a power supply for his SSD!",
|
||||||
|
"Superman dies",
|
||||||
|
"PP when?",
|
||||||
|
"RWC hype",
|
||||||
}
|
}
|
||||||
|
|
||||||
func surpriseMe() string {
|
func surpriseMe() string {
|
||||||
return randomSentences[rn.Intn(len(randomSentences))] + " " + kaomojis[rn.Intn(len(kaomojis))]
|
return randomSentences[rn.Intn(len(randomSentences))] + " " + kaomojis[rn.Intn(len(kaomojis))]
|
||||||
}
|
}
|
||||||
|
|
||||||
type pingData struct {
|
type pingResponse struct {
|
||||||
|
common.ResponseBase
|
||||||
ID int `json:"user_id"`
|
ID int `json:"user_id"`
|
||||||
Privileges int `json:"privileges"`
|
Privileges int `json:"privileges"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PingGET is a message to check with the API that we are logged in, and know what are our privileges.
|
// PingGET is a message to check with the API that we are logged in, and know what are our privileges.
|
||||||
func PingGET(md common.MethodData) (r common.Response) {
|
func PingGET(md common.MethodData) common.CodeMessager {
|
||||||
|
var r pingResponse
|
||||||
r.Code = 200
|
r.Code = 200
|
||||||
|
|
||||||
if md.ID() == 0 {
|
if md.ID() == 0 {
|
||||||
r.Message = "You have not given us a token, so we don't know who you are! But you can still login with /api/v1/tokens/new " + kaomojis[rn.Intn(len(kaomojis))]
|
r.Message = "You have not given us a token, so we don't know who you are! But you can still login with /api/v1/tokens/new " + kaomojis[rn.Intn(len(kaomojis))]
|
||||||
} else {
|
} else {
|
||||||
r.Message = surpriseMe()
|
r.Message = surpriseMe()
|
||||||
}
|
}
|
||||||
r.Data = pingData{
|
|
||||||
ID: md.ID(),
|
r.ID = md.ID()
|
||||||
Privileges: int(md.User.Privileges),
|
r.Privileges = int(md.User.Privileges)
|
||||||
}
|
|
||||||
return
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
type surpriseMeResponse struct {
|
||||||
|
common.ResponseBase
|
||||||
|
Cats [100]string `json:"cats"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SurpriseMeGET generates cute cats.
|
// SurpriseMeGET generates cute cats.
|
||||||
//
|
//
|
||||||
// ... Yes.
|
// ... Yes.
|
||||||
func SurpriseMeGET(md common.MethodData) (r common.Response) {
|
func SurpriseMeGET(md common.MethodData) common.CodeMessager {
|
||||||
|
var r surpriseMeResponse
|
||||||
r.Code = 200
|
r.Code = 200
|
||||||
cats := make([]string, 100)
|
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
cats[i] = surpriseMe()
|
r.Cats[i] = surpriseMe()
|
||||||
}
|
}
|
||||||
r.Data = cats
|
return r
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type privilegesData struct {
|
type privilegesData struct {
|
||||||
|
common.ResponseBase
|
||||||
Read bool `json:"read"`
|
Read bool `json:"read"`
|
||||||
ReadConfidential bool `json:"read_confidential"`
|
ReadConfidential bool `json:"read_confidential"`
|
||||||
Write bool `json:"write"`
|
Write bool `json:"write"`
|
||||||
|
@ -20,22 +21,21 @@ type privilegesData struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrivilegesGET returns an explaination for the privileges, telling the client what they can do with this token.
|
// PrivilegesGET returns an explaination for the privileges, telling the client what they can do with this token.
|
||||||
func PrivilegesGET(md common.MethodData) (r common.Response) {
|
func PrivilegesGET(md common.MethodData) common.CodeMessager {
|
||||||
// This code sucks.
|
r := privilegesData{}
|
||||||
r.Code = 200
|
r.Code = 200
|
||||||
r.Data = privilegesData{
|
// This code sucks.
|
||||||
Read: md.User.Privileges.HasPrivilegeRead(),
|
r.Read = md.User.Privileges.HasPrivilegeRead()
|
||||||
ReadConfidential: md.User.Privileges.HasPrivilegeReadConfidential(),
|
r.ReadConfidential = md.User.Privileges.HasPrivilegeReadConfidential()
|
||||||
Write: md.User.Privileges.HasPrivilegeWrite(),
|
r.Write = md.User.Privileges.HasPrivilegeWrite()
|
||||||
ManageBadges: md.User.Privileges.HasPrivilegeManageBadges(),
|
r.ManageBadges = md.User.Privileges.HasPrivilegeManageBadges()
|
||||||
BetaKeys: md.User.Privileges.HasPrivilegeBetaKeys(),
|
r.BetaKeys = md.User.Privileges.HasPrivilegeBetaKeys()
|
||||||
ManageSettings: md.User.Privileges.HasPrivilegeManageSettings(),
|
r.ManageSettings = md.User.Privileges.HasPrivilegeManageSettings()
|
||||||
ViewUserAdvanced: md.User.Privileges.HasPrivilegeViewUserAdvanced(),
|
r.ViewUserAdvanced = md.User.Privileges.HasPrivilegeViewUserAdvanced()
|
||||||
ManageUser: md.User.Privileges.HasPrivilegeManageUser(),
|
r.ManageUser = md.User.Privileges.HasPrivilegeManageUser()
|
||||||
ManageRoles: md.User.Privileges.HasPrivilegeManageRoles(),
|
r.ManageRoles = md.User.Privileges.HasPrivilegeManageRoles()
|
||||||
ManageAPIKeys: md.User.Privileges.HasPrivilegeManageAPIKeys(),
|
r.ManageAPIKeys = md.User.Privileges.HasPrivilegeManageAPIKeys()
|
||||||
Blog: md.User.Privileges.HasPrivilegeBlog(),
|
r.Blog = md.User.Privileges.HasPrivilegeBlog()
|
||||||
APIMeta: md.User.Privileges.HasPrivilegeAPIMeta(),
|
r.APIMeta = md.User.Privileges.HasPrivilegeAPIMeta()
|
||||||
}
|
return r
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,8 @@ type tokenNewInData struct {
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type tokenNewOutData struct {
|
type tokenNewResponse struct {
|
||||||
|
common.ResponseBase
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Privileges int `json:"privileges"`
|
Privileges int `json:"privileges"`
|
||||||
|
@ -28,12 +29,12 @@ type tokenNewOutData struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TokenNewPOST is the handler for POST /token/new.
|
// TokenNewPOST is the handler for POST /token/new.
|
||||||
func TokenNewPOST(md common.MethodData) (r common.Response) {
|
func TokenNewPOST(md common.MethodData) common.CodeMessager {
|
||||||
|
var r tokenNewResponse
|
||||||
data := tokenNewInData{}
|
data := tokenNewInData{}
|
||||||
err := md.RequestData.Unmarshal(&data)
|
err := md.RequestData.Unmarshal(&data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r = ErrBadJSON
|
return ErrBadJSON
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var miss []string
|
var miss []string
|
||||||
|
@ -44,8 +45,7 @@ func TokenNewPOST(md common.MethodData) (r common.Response) {
|
||||||
miss = append(miss, "password")
|
miss = append(miss, "password")
|
||||||
}
|
}
|
||||||
if len(miss) != 0 {
|
if len(miss) != 0 {
|
||||||
r = ErrMissingField(miss...)
|
return ErrMissingField(miss...)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var q *sql.Row
|
var q *sql.Row
|
||||||
|
@ -56,7 +56,6 @@ func TokenNewPOST(md common.MethodData) (r common.Response) {
|
||||||
q = md.DB.QueryRow(base+"WHERE username = ? LIMIT 1", data.Username)
|
q = md.DB.QueryRow(base+"WHERE username = ? LIMIT 1", data.Username)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret := tokenNewOutData{}
|
|
||||||
var (
|
var (
|
||||||
rank int
|
rank int
|
||||||
pw string
|
pw string
|
||||||
|
@ -64,48 +63,37 @@ func TokenNewPOST(md common.MethodData) (r common.Response) {
|
||||||
allowed int
|
allowed int
|
||||||
)
|
)
|
||||||
|
|
||||||
err = q.Scan(&ret.ID, &ret.Username, &rank, &pw, &pwVersion, &allowed)
|
err = q.Scan(&r.ID, &r.Username, &rank, &pw, &pwVersion, &allowed)
|
||||||
switch {
|
switch {
|
||||||
case err == sql.ErrNoRows:
|
case err == sql.ErrNoRows:
|
||||||
r.Code = 404
|
return common.SimpleResponse(404, "No user with that username/id was found.")
|
||||||
r.Message = "No user with that username/id was found."
|
|
||||||
return
|
|
||||||
case err != nil:
|
case err != nil:
|
||||||
md.Err(err)
|
md.Err(err)
|
||||||
r = Err500
|
return Err500
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if nFailedAttempts(ret.ID) > 20 {
|
if nFailedAttempts(r.ID) > 20 {
|
||||||
r.Code = 429
|
return common.SimpleResponse(429, "You've made too many login attempts. Try again later.")
|
||||||
r.Message = "You've made too many login attempts. Try again later."
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if pwVersion == 1 {
|
if pwVersion == 1 {
|
||||||
r.Code = 418 // Teapots!
|
return common.SimpleResponse(418, "That user still has a password in version 1. Unfortunately, in order for the API to check for the password to be OK, the user has to first log in through the website.")
|
||||||
r.Message = "That user still has a password in version 1. Unfortunately, in order for the API to check for the password to be OK, the user has to first log in through the website."
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if err := bcrypt.CompareHashAndPassword([]byte(pw), []byte(fmt.Sprintf("%x", md5.Sum([]byte(data.Password))))); err != nil {
|
if err := bcrypt.CompareHashAndPassword([]byte(pw), []byte(fmt.Sprintf("%x", md5.Sum([]byte(data.Password))))); err != nil {
|
||||||
if err == bcrypt.ErrMismatchedHashAndPassword {
|
if err == bcrypt.ErrMismatchedHashAndPassword {
|
||||||
go addFailedAttempt(ret.ID)
|
go addFailedAttempt(r.ID)
|
||||||
r.Code = 403
|
return common.SimpleResponse(403, "That password doesn't match!")
|
||||||
r.Message = "That password doesn't match!"
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
md.Err(err)
|
md.Err(err)
|
||||||
r = Err500
|
return Err500
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if allowed == 0 {
|
if allowed == 0 {
|
||||||
r.Code = 200
|
r.Code = 200
|
||||||
r.Message = "That user is banned."
|
r.Message = "That user is banned."
|
||||||
ret.Banned = true
|
r.Banned = true
|
||||||
r.Data = ret
|
return r
|
||||||
return
|
|
||||||
}
|
}
|
||||||
ret.Privileges = int(common.Privileges(data.Privileges).CanOnly(rank))
|
r.Privileges = int(common.Privileges(data.Privileges).CanOnly(rank))
|
||||||
|
|
||||||
var (
|
var (
|
||||||
tokenStr string
|
tokenStr string
|
||||||
|
@ -114,7 +102,7 @@ func TokenNewPOST(md common.MethodData) (r common.Response) {
|
||||||
for {
|
for {
|
||||||
tokenStr = common.RandomString(32)
|
tokenStr = common.RandomString(32)
|
||||||
tokenMD5 = fmt.Sprintf("%x", md5.Sum([]byte(tokenStr)))
|
tokenMD5 = fmt.Sprintf("%x", md5.Sum([]byte(tokenStr)))
|
||||||
ret.Token = tokenStr
|
r.Token = tokenStr
|
||||||
id := 0
|
id := 0
|
||||||
|
|
||||||
err := md.DB.QueryRow("SELECT id FROM tokens WHERE token=? LIMIT 1", tokenMD5).Scan(&id)
|
err := md.DB.QueryRow("SELECT id FROM tokens WHERE token=? LIMIT 1", tokenMD5).Scan(&id)
|
||||||
|
@ -123,18 +111,15 @@ func TokenNewPOST(md common.MethodData) (r common.Response) {
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
md.Err(err)
|
md.Err(err)
|
||||||
r = Err500
|
return Err500
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err = md.DB.Exec("INSERT INTO tokens(user, privileges, description, token) VALUES (?, ?, ?, ?)", ret.ID, ret.Privileges, data.Description, tokenMD5)
|
_, err = md.DB.Exec("INSERT INTO tokens(user, privileges, description, token) VALUES (?, ?, ?, ?)", r.ID, r.Privileges, data.Description, tokenMD5)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
md.Err(err)
|
md.Err(err)
|
||||||
r = Err500
|
return Err500
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r.Code = 200
|
r.Code = 200
|
||||||
r.Data = ret
|
return r
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
128
app/v1/user.go
128
app/v1/user.go
|
@ -13,6 +13,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type userData struct {
|
type userData struct {
|
||||||
|
common.ResponseBase
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
UsernameAKA string `json:"username_aka"`
|
UsernameAKA string `json:"username_aka"`
|
||||||
|
@ -23,7 +24,7 @@ type userData struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserByIDGET is the API handler for GET /users/id/:id
|
// UserByIDGET is the API handler for GET /users/id/:id
|
||||||
func UserByIDGET(md common.MethodData) (r common.Response) {
|
func UserByIDGET(md common.MethodData) common.CodeMessager {
|
||||||
var err error
|
var err error
|
||||||
var uid int
|
var uid int
|
||||||
uidStr := md.C.Param("id")
|
uidStr := md.C.Param("id")
|
||||||
|
@ -32,9 +33,7 @@ func UserByIDGET(md common.MethodData) (r common.Response) {
|
||||||
} else {
|
} else {
|
||||||
uid, err = strconv.Atoi(uidStr)
|
uid, err = strconv.Atoi(uidStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Code = 400
|
return common.SimpleResponse(400, fmt.Sprintf("%s ain't a number", uidStr))
|
||||||
r.Message = fmt.Sprintf("%s ain't a number", uidStr)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,12 +46,11 @@ LEFT JOIN users_stats
|
||||||
ON users.id=users_stats.id
|
ON users.id=users_stats.id
|
||||||
WHERE users.id=? AND users.allowed='1'
|
WHERE users.id=? AND users.allowed='1'
|
||||||
LIMIT 1`
|
LIMIT 1`
|
||||||
r = userPuts(md, md.DB.QueryRow(query, uid))
|
return userPuts(md, md.DB.QueryRow(query, uid))
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserByNameGET is the API handler for GET /users/name/:name
|
// UserByNameGET is the API handler for GET /users/name/:name
|
||||||
func UserByNameGET(md common.MethodData) (r common.Response) {
|
func UserByNameGET(md common.MethodData) common.CodeMessager {
|
||||||
username := md.C.Param("name")
|
username := md.C.Param("name")
|
||||||
|
|
||||||
query := `
|
query := `
|
||||||
|
@ -64,11 +62,10 @@ LEFT JOIN users_stats
|
||||||
ON users.id=users_stats.id
|
ON users.id=users_stats.id
|
||||||
WHERE users.username=? AND users.allowed='1'
|
WHERE users.username=? AND users.allowed='1'
|
||||||
LIMIT 1`
|
LIMIT 1`
|
||||||
r = userPuts(md, md.DB.QueryRow(query, username))
|
return userPuts(md, md.DB.QueryRow(query, username))
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func userPuts(md common.MethodData, row *sql.Row) (r common.Response) {
|
func userPuts(md common.MethodData, row *sql.Row) common.CodeMessager {
|
||||||
var err error
|
var err error
|
||||||
var user userData
|
var user userData
|
||||||
|
|
||||||
|
@ -80,13 +77,10 @@ func userPuts(md common.MethodData, row *sql.Row) (r common.Response) {
|
||||||
err = row.Scan(&user.ID, &user.Username, ®isteredOn, &user.Rank, &latestActivity, &user.UsernameAKA, &user.Country, &showCountry)
|
err = row.Scan(&user.ID, &user.Username, ®isteredOn, &user.Rank, &latestActivity, &user.UsernameAKA, &user.Country, &showCountry)
|
||||||
switch {
|
switch {
|
||||||
case err == sql.ErrNoRows:
|
case err == sql.ErrNoRows:
|
||||||
r.Code = 404
|
return common.SimpleResponse(404, "No such user was found!")
|
||||||
r.Message = "No such user was found!"
|
|
||||||
return
|
|
||||||
case err != nil:
|
case err != nil:
|
||||||
md.Err(err)
|
md.Err(err)
|
||||||
r = Err500
|
return Err500
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
user.RegisteredOn = time.Unix(registeredOn, 0)
|
user.RegisteredOn = time.Unix(registeredOn, 0)
|
||||||
|
@ -94,9 +88,8 @@ func userPuts(md common.MethodData, row *sql.Row) (r common.Response) {
|
||||||
|
|
||||||
user.Country = genCountry(md, user.ID, showCountry, user.Country)
|
user.Country = genCountry(md, user.ID, showCountry, user.Country)
|
||||||
|
|
||||||
r.Code = 200
|
user.Code = 200
|
||||||
r.Data = user
|
return user
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func badgesToArray(badges string) []int {
|
func badgesToArray(badges string) []int {
|
||||||
|
@ -125,7 +118,7 @@ func genCountry(md common.MethodData, uid int, showCountry bool, country string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserSelfGET is a shortcut for /users/id/self. (/users/self)
|
// UserSelfGET is a shortcut for /users/id/self. (/users/self)
|
||||||
func UserSelfGET(md common.MethodData) common.Response {
|
func UserSelfGET(md common.MethodData) common.CodeMessager {
|
||||||
md.C.Params = append(md.C.Params, gin.Param{
|
md.C.Params = append(md.C.Params, gin.Param{
|
||||||
Key: "id",
|
Key: "id",
|
||||||
Value: "self",
|
Value: "self",
|
||||||
|
@ -133,23 +126,23 @@ func UserSelfGET(md common.MethodData) common.Response {
|
||||||
return UserByIDGET(md)
|
return UserByIDGET(md)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type whatIDResponse struct {
|
||||||
|
common.ResponseBase
|
||||||
|
ID int `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
// UserWhatsTheIDGET is an API request that only returns an user's ID.
|
// UserWhatsTheIDGET is an API request that only returns an user's ID.
|
||||||
func UserWhatsTheIDGET(md common.MethodData) common.Response {
|
func UserWhatsTheIDGET(md common.MethodData) common.CodeMessager {
|
||||||
var (
|
var (
|
||||||
id int
|
r whatIDResponse
|
||||||
allowed int
|
allowed int
|
||||||
)
|
)
|
||||||
err := md.DB.QueryRow("SELECT id, allowed FROM users WHERE username = ? LIMIT 1", md.C.Param("username")).Scan(&id, &allowed)
|
err := md.DB.QueryRow("SELECT id, allowed FROM users WHERE username = ? LIMIT 1", md.C.Param("username")).Scan(&r.ID, &allowed)
|
||||||
if err != nil || (allowed != 1 && !md.User.Privileges.HasPrivilegeViewUserAdvanced()) {
|
if err != nil || (allowed != 1 && !md.User.Privileges.HasPrivilegeViewUserAdvanced()) {
|
||||||
return common.Response{
|
return common.SimpleResponse(404, "That user could not be found!")
|
||||||
Code: 404,
|
|
||||||
Message: "That user could not be found!",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return common.Response{
|
|
||||||
Code: 200,
|
|
||||||
Data: id,
|
|
||||||
}
|
}
|
||||||
|
r.Code = 200
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
type modeData struct {
|
type modeData struct {
|
||||||
|
@ -162,7 +155,8 @@ type modeData struct {
|
||||||
Accuracy float64 `json:"accuracy"`
|
Accuracy float64 `json:"accuracy"`
|
||||||
GlobalLeaderboardRank int `json:"global_leaderboard_rank"`
|
GlobalLeaderboardRank int `json:"global_leaderboard_rank"`
|
||||||
}
|
}
|
||||||
type userFullData struct {
|
type userFullResponse struct {
|
||||||
|
common.ResponseBase
|
||||||
userData
|
userData
|
||||||
STD modeData `json:"std"`
|
STD modeData `json:"std"`
|
||||||
Taiko modeData `json:"taiko"`
|
Taiko modeData `json:"taiko"`
|
||||||
|
@ -174,7 +168,7 @@ type userFullData struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserFullGET gets all of an user's information, with one exception: their userpage.
|
// UserFullGET gets all of an user's information, with one exception: their userpage.
|
||||||
func UserFullGET(md common.MethodData) (r common.Response) {
|
func UserFullGET(md common.MethodData) common.CodeMessager {
|
||||||
// Hellest query I've ever done.
|
// Hellest query I've ever done.
|
||||||
query := `
|
query := `
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -214,7 +208,7 @@ WHERE users.id=? AND users.allowed = '1'
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
`
|
`
|
||||||
// Fuck.
|
// Fuck.
|
||||||
fd := userFullData{}
|
r := userFullResponse{}
|
||||||
var (
|
var (
|
||||||
badges string
|
badges string
|
||||||
country string
|
country string
|
||||||
|
@ -223,63 +217,61 @@ LIMIT 1
|
||||||
latestActivity int64
|
latestActivity int64
|
||||||
)
|
)
|
||||||
err := md.DB.QueryRow(query, md.C.Param("id")).Scan(
|
err := md.DB.QueryRow(query, md.C.Param("id")).Scan(
|
||||||
&fd.ID, &fd.Username, ®isteredOn, &fd.Rank, &latestActivity,
|
&r.ID, &r.Username, ®isteredOn, &r.Rank, &latestActivity,
|
||||||
|
|
||||||
&fd.UsernameAKA, &badges, &country, &showCountry,
|
&r.UsernameAKA, &badges, &country, &showCountry,
|
||||||
&fd.PlayStyle, &fd.FavouriteMode,
|
&r.PlayStyle, &r.FavouriteMode,
|
||||||
|
|
||||||
&fd.STD.RankedScore, &fd.STD.TotalScore, &fd.STD.PlayCount,
|
&r.STD.RankedScore, &r.STD.TotalScore, &r.STD.PlayCount,
|
||||||
&fd.STD.ReplaysWatched, &fd.STD.TotalHits, &fd.STD.Level,
|
&r.STD.ReplaysWatched, &r.STD.TotalHits, &r.STD.Level,
|
||||||
&fd.STD.Accuracy, &fd.STD.GlobalLeaderboardRank,
|
&r.STD.Accuracy, &r.STD.GlobalLeaderboardRank,
|
||||||
|
|
||||||
&fd.Taiko.RankedScore, &fd.Taiko.TotalScore, &fd.Taiko.PlayCount,
|
&r.Taiko.RankedScore, &r.Taiko.TotalScore, &r.Taiko.PlayCount,
|
||||||
&fd.Taiko.ReplaysWatched, &fd.Taiko.TotalHits, &fd.Taiko.Level,
|
&r.Taiko.ReplaysWatched, &r.Taiko.TotalHits, &r.Taiko.Level,
|
||||||
&fd.Taiko.Accuracy, &fd.Taiko.GlobalLeaderboardRank,
|
&r.Taiko.Accuracy, &r.Taiko.GlobalLeaderboardRank,
|
||||||
|
|
||||||
&fd.CTB.RankedScore, &fd.CTB.TotalScore, &fd.CTB.PlayCount,
|
&r.CTB.RankedScore, &r.CTB.TotalScore, &r.CTB.PlayCount,
|
||||||
&fd.CTB.ReplaysWatched, &fd.CTB.TotalHits, &fd.CTB.Level,
|
&r.CTB.ReplaysWatched, &r.CTB.TotalHits, &r.CTB.Level,
|
||||||
&fd.CTB.Accuracy, &fd.CTB.GlobalLeaderboardRank,
|
&r.CTB.Accuracy, &r.CTB.GlobalLeaderboardRank,
|
||||||
|
|
||||||
&fd.Mania.RankedScore, &fd.Mania.TotalScore, &fd.Mania.PlayCount,
|
&r.Mania.RankedScore, &r.Mania.TotalScore, &r.Mania.PlayCount,
|
||||||
&fd.Mania.ReplaysWatched, &fd.Mania.TotalHits, &fd.Mania.Level,
|
&r.Mania.ReplaysWatched, &r.Mania.TotalHits, &r.Mania.Level,
|
||||||
&fd.Mania.Accuracy, &fd.Mania.GlobalLeaderboardRank,
|
&r.Mania.Accuracy, &r.Mania.GlobalLeaderboardRank,
|
||||||
)
|
)
|
||||||
switch {
|
switch {
|
||||||
case err == sql.ErrNoRows:
|
case err == sql.ErrNoRows:
|
||||||
r.Code = 404
|
return common.SimpleResponse(404, "That user could not be found!")
|
||||||
r.Message = "That user could not be found!"
|
|
||||||
return
|
|
||||||
case err != nil:
|
case err != nil:
|
||||||
md.Err(err)
|
md.Err(err)
|
||||||
r = Err500
|
return Err500
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fd.Country = genCountry(md, fd.ID, showCountry, country)
|
r.Country = genCountry(md, r.ID, showCountry, country)
|
||||||
fd.Badges = badgesToArray(badges)
|
r.Badges = badgesToArray(badges)
|
||||||
|
|
||||||
fd.RegisteredOn = time.Unix(registeredOn, 0)
|
r.RegisteredOn = time.Unix(registeredOn, 0)
|
||||||
fd.LatestActivity = time.Unix(latestActivity, 0)
|
r.LatestActivity = time.Unix(latestActivity, 0)
|
||||||
|
|
||||||
r.Code = 200
|
r.Code = 200
|
||||||
r.Data = fd
|
return r
|
||||||
return
|
}
|
||||||
|
|
||||||
|
type userpageResponse struct {
|
||||||
|
common.ResponseBase
|
||||||
|
Userpage string `json:"userpage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserUserpageGET gets an user's userpage, as in the customisable thing.
|
// UserUserpageGET gets an user's userpage, as in the customisable thing.
|
||||||
func UserUserpageGET(md common.MethodData) (r common.Response) {
|
func UserUserpageGET(md common.MethodData) common.CodeMessager {
|
||||||
var userpage string
|
var r userpageResponse
|
||||||
err := md.DB.QueryRow("SELECT userpage_content FROM users_stats WHERE id = ? LIMIT 1", md.C.Param("id")).Scan(&userpage)
|
err := md.DB.QueryRow("SELECT userpage_content FROM users_stats WHERE id = ? LIMIT 1", md.C.Param("id")).Scan(&r.Userpage)
|
||||||
switch {
|
switch {
|
||||||
case err == sql.ErrNoRows:
|
case err == sql.ErrNoRows:
|
||||||
r.Code = 404
|
return common.SimpleResponse(404, "No user with that user ID!")
|
||||||
r.Message = "No user with that user ID!"
|
|
||||||
case err != nil:
|
case err != nil:
|
||||||
md.Err(err)
|
md.Err(err)
|
||||||
r = Err500
|
return Err500
|
||||||
return
|
|
||||||
}
|
}
|
||||||
r.Code = 200
|
r.Code = 200
|
||||||
r.Data = userpage
|
return r
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,36 @@
|
||||||
package common
|
package common
|
||||||
|
|
||||||
// Response is the data that is always returned with an API request.
|
// ResponseBase is the data that is always returned with an API request.
|
||||||
type Response struct {
|
type ResponseBase struct {
|
||||||
Code int `json:"code"`
|
Code int `json:"code"`
|
||||||
Message string `json:"message,omitempty"`
|
Message string `json:"message,omitempty"`
|
||||||
Data interface{} `json:"data"`
|
}
|
||||||
|
|
||||||
|
// GetCode retrieves the response code.
|
||||||
|
func (r ResponseBase) GetCode() int {
|
||||||
|
return r.Code
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCode changes the response code.
|
||||||
|
func (r *ResponseBase) SetCode(i int) {
|
||||||
|
r.Code = i
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMessage retrieves the response message.
|
||||||
|
func (r ResponseBase) GetMessage() string {
|
||||||
|
return r.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodeMessager is something that has the Code() and Message() methods.
|
||||||
|
type CodeMessager interface {
|
||||||
|
GetMessage() string
|
||||||
|
GetCode() int
|
||||||
|
}
|
||||||
|
|
||||||
|
// SimpleResponse returns the most basic response.
|
||||||
|
func SimpleResponse(code int, message string) CodeMessager {
|
||||||
|
return ResponseBase{
|
||||||
|
Code: code,
|
||||||
|
Message: message,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user