From 44d12d2493030d14406fd8a13ae2bb8ea5a92991 Mon Sep 17 00:00:00 2001 From: Howl Date: Fri, 8 Apr 2016 19:05:54 +0200 Subject: [PATCH] Ability to add friends. Also, made a few helper functions. --- app/start.go | 4 ++ app/v1/badge.go | 8 ++-- app/v1/friend.go | 98 ++++++++++++++++++++++++++++++++++++++----- app/v1/ping.go | 4 +- app/v1/token.go | 8 ++-- app/v1/user.go | 10 ++--- common/method_data.go | 10 +++++ 7 files changed, 116 insertions(+), 26 deletions(-) diff --git a/app/start.go b/app/start.go index e071471..9aca04a 100644 --- a/app/start.go +++ b/app/start.go @@ -39,6 +39,10 @@ func Start(conf common.Conf, db *sql.DB) *gin.Engine { gv1.GET("/friends", Method(v1.FriendsGET, db, common.PrivilegeReadConfidential)) gv1.GET("/friends/with/:id", Method(v1.FriendsWithGET, db, common.PrivilegeReadConfidential)) + // Write privilege required + gv1.POST("/friends/add", Method(v1.FriendsAddPOST, db, common.PrivilegeWrite)) + gv1.GET("/friends/add/:id", Method(v1.FriendsAddGET, db, common.PrivilegeWrite)) + // M E T A // E T "wow thats so meta" // T E -- the one who said "wow that's so meta" diff --git a/app/v1/badge.go b/app/v1/badge.go index e8a9b6e..001d8b4 100644 --- a/app/v1/badge.go +++ b/app/v1/badge.go @@ -22,7 +22,7 @@ func BadgeByIDGET(md common.MethodData) (r common.Response) { r.Message = "No such badge was found" return case err != nil: - md.C.Error(err) + md.Err(err) r = Err500 return } @@ -36,7 +36,7 @@ func BadgesGET(md common.MethodData) (r common.Response) { var badges []badgeData rows, err := md.DB.Query("SELECT id, name, icon FROM badges") if err != nil { - md.C.Error(err) + md.Err(err) r = Err500 return } @@ -45,12 +45,12 @@ func BadgesGET(md common.MethodData) (r common.Response) { nb := badgeData{} err = rows.Scan(&nb.ID, &nb.Name, &nb.Icon) if err != nil { - md.C.Error(err) + md.Err(err) } badges = append(badges, nb) } if err := rows.Err(); err != nil { - md.C.Error(err) + md.Err(err) } r.Code = 200 r.Data = badges diff --git a/app/v1/friend.go b/app/v1/friend.go index b63bd62..cd9cced 100644 --- a/app/v1/friend.go +++ b/app/v1/friend.go @@ -2,6 +2,7 @@ package v1 import ( "database/sql" + "encoding/json" "strconv" "strings" "time" @@ -18,9 +19,9 @@ type friendData struct { // It retrieves an user's friends, and whether the friendship is mutual or not. func FriendsGET(md common.MethodData) (r common.Response) { var myFrienders []int - myFriendersRaw, err := md.DB.Query("SELECT user1 FROM users_relationships WHERE user2 = ?", md.User.UserID) + myFriendersRaw, err := md.DB.Query("SELECT user1 FROM users_relationships WHERE user2 = ?", md.ID()) if err != nil { - md.C.Error(err) + md.Err(err) r = Err500 return } @@ -29,13 +30,13 @@ func FriendsGET(md common.MethodData) (r common.Response) { var i int err := myFriendersRaw.Scan(&i) if err != nil { - md.C.Error(err) + md.Err(err) continue } myFrienders = append(myFrienders, i) } if err := myFriendersRaw.Err(); err != nil { - md.C.Error(err) + md.Err(err) } // Yes. @@ -53,9 +54,9 @@ ON users_relationships.user2=users_stats.id WHERE users_relationships.user1=? ORDER BY users_relationships.id` - results, err := md.DB.Query(myFriendsQuery+common.Paginate(md.C.Query("p"), md.C.Query("l")), md.User.UserID) + results, err := md.DB.Query(myFriendsQuery+common.Paginate(md.C.Query("p"), md.C.Query("l")), md.ID()) if err != nil { - md.C.Error(err) + md.Err(err) r = Err500 return } @@ -74,7 +75,7 @@ ORDER BY users_relationships.id` myFriends = append(myFriends, newFriend) } if err := results.Err(); err != nil { - md.C.Error(err) + md.Err(err) } r.Code = 200 @@ -91,7 +92,7 @@ func friendPuts(md common.MethodData, row *sql.Rows) (user friendData) { var showcountry bool err = row.Scan(&user.ID, &user.Username, ®isteredOn, &user.Rank, &latestActivity, &user.UsernameAKA, &badges, &user.Country, &showcountry) if err != nil { - md.C.Error(err) + md.Err(err) return } @@ -112,7 +113,7 @@ func friendPuts(md common.MethodData, row *sql.Rows) (user friendData) { // If the user wants to stay anonymous, don't show their country. // This can be overriden if we have the ReadConfidential privilege and the user we are accessing is the token owner. - if !(showcountry || (md.User.Privileges.HasPrivilegeReadConfidential() && user.ID == md.User.UserID)) { + if !(showcountry || (md.User.Privileges.HasPrivilegeReadConfidential() && user.ID == md.ID())) { user.Country = "XX" } return @@ -132,12 +133,87 @@ func FriendsWithGET(md common.MethodData) (r common.Response) { r.Data = d 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.User.UserID, uid, md.User.UserID, 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(&d.Friends, &d.Mutual) if err != sql.ErrNoRows && err != nil { - md.C.Error(err) + md.Err(err) r = Err500 return } r.Data = d return } + +// FriendsAddGET is the GET version of FriendsAddPOST. +func FriendsAddGET(md common.MethodData) common.Response { + uidS := md.C.Param("id") + uid, err := strconv.Atoi(uidS) + if err != nil { + return common.Response{ + Code: 400, + Message: "Nope. That's not a number.", + } + } + return addFriend(md, uid) +} + +type friendAddPOSTData struct { + UserID int `json:"user_id"` +} + +// FriendsAddPOST allows for adding friends. Yup. Easy as that. +func FriendsAddPOST(md common.MethodData) (r common.Response) { + d := friendAddPOSTData{} + err := json.Unmarshal(md.RequestData, &d) + if err != nil { + md.Err(err) + r = Err500 + return + } + return addFriend(md, d.UserID) +} + +func addFriend(md common.MethodData, u int) (r common.Response) { + if md.ID() == u { + r.Code = 400 + r.Message = "Just so you know: you can't add yourself to your friends." + return + } + if !userExists(md, u) { + r.Code = 404 + r.Message = "I'd also like to be friends with someone who doesn't even exist (???), however that's NOT POSSIBLE." + return + } + var ( + relExists bool + isMutual bool + ) + 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 { + md.Err(err) + r = Err500 + return + } + if !relExists { + _, err := md.DB.Exec("INSERT INTO users_relationships(user1, user2) VALUES (?, ?)", md.User.UserID, u) + if err != nil { + md.Err(err) + r = Err500 + return + } + } + r.Code = 200 + r.Data = friendsWithData{ + Friends: relExists, + Mutual: isMutual, + } + return +} + +// userExists makes sure an user exists. +func userExists(md common.MethodData, u int) (r bool) { + err := md.DB.QueryRow("SELECT EXISTS(SELECT 1 FROM users WHERE id = ?)", u).Scan(&r) + if err != nil && err != sql.ErrNoRows { + md.Err(err) + } + return +} diff --git a/app/v1/ping.go b/app/v1/ping.go index f274433..390c4d7 100644 --- a/app/v1/ping.go +++ b/app/v1/ping.go @@ -90,13 +90,13 @@ type pingData struct { // 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) { r.Code = 200 - if md.User.UserID == 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/login " + kaomojis[rn.Intn(len(kaomojis))] } else { r.Message = surpriseMe() } r.Data = pingData{ - ID: md.User.UserID, + ID: md.ID(), Privileges: int(md.User.Privileges), } return diff --git a/app/v1/token.go b/app/v1/token.go index 9179245..2d2d277 100644 --- a/app/v1/token.go +++ b/app/v1/token.go @@ -72,7 +72,7 @@ func TokenNewPOST(md common.MethodData) (r common.Response) { r.Message = "No user with that username/id was found." return case err != nil: - md.C.Error(err) + md.Err(err) r = Err500 return } @@ -88,7 +88,7 @@ func TokenNewPOST(md common.MethodData) (r common.Response) { r.Message = "That password doesn't match!" return } - md.C.Error(err) + md.Err(err) r = Err500 return } @@ -116,14 +116,14 @@ func TokenNewPOST(md common.MethodData) (r common.Response) { break } if err != nil { - md.C.Error(err) + md.Err(err) r = Err500 return } } _, err = md.DB.Exec("INSERT INTO tokens(user, privileges, description, token) VALUES (?, ?, ?, ?)", ret.ID, ret.Privileges, data.Description, tokenMD5) if err != nil { - md.C.Error(err) + md.Err(err) r = Err500 return } diff --git a/app/v1/user.go b/app/v1/user.go index 88a3203..2b28b3b 100644 --- a/app/v1/user.go +++ b/app/v1/user.go @@ -29,7 +29,7 @@ func UserByIDGET(md common.MethodData) (r common.Response) { var uid int uidStr := md.C.Param("id") if uidStr == "self" { - uid = md.User.UserID + uid = md.ID() } else { uid, err = strconv.Atoi(uidStr) if err != nil { @@ -86,7 +86,7 @@ func userPuts(md common.MethodData, row *sql.Row) (r common.Response) { r.Message = "No such user was found!" return case err != nil: - md.C.Error(err) + md.Err(err) r = Err500 return } @@ -122,7 +122,7 @@ func badgesToArray(badges string) []int { func genCountry(md common.MethodData, uid int, showCountry bool, country string) string { // If the user wants to stay anonymous, don't show their country. // This can be overriden if we have the ReadConfidential privilege and the user we are accessing is the token owner. - if showCountry || (md.User.Privileges.HasPrivilegeReadConfidential() && uid == md.User.UserID) { + if showCountry || (md.User.Privileges.HasPrivilegeReadConfidential() && uid == md.ID()) { return country } return "XX" @@ -253,7 +253,7 @@ LIMIT 1 r.Message = "That user could not be found!" return case err != nil: - md.C.Error(err) + md.Err(err) r = Err500 return } @@ -278,7 +278,7 @@ func UserUserpageGET(md common.MethodData) (r common.Response) { r.Code = 404 r.Message = "No user with that user ID!" case err != nil: - md.C.Error(err) + md.Err(err) r = Err500 return } diff --git a/common/method_data.go b/common/method_data.go index d2f50eb..ba819d1 100644 --- a/common/method_data.go +++ b/common/method_data.go @@ -13,3 +13,13 @@ type MethodData struct { RequestData []byte C *gin.Context } + +// Err logs an error into gin. +func (md MethodData) Err(err error) { + md.C.Error(err) +} + +// ID retrieves the Token's owner user ID. +func (md MethodData) ID() int { + return md.User.UserID +}