From 16da4451970ec6190decfae05892f78a214bada5 Mon Sep 17 00:00:00 2001 From: robyzzz Date: Fri, 4 Mar 2022 01:24:40 +0000 Subject: [PATCH] code refactor --- config/store.go | 16 +++++---- controller/{login.go => auth.go} | 8 +++-- controller/player.go | 23 ------------- controller/steam_user.go | 7 ++-- controller/user.go | 50 ++++++++++++++++++++++++++++ main.go | 19 ++++++----- middleware/auth.go | 18 ++++++++-- model/db.go | 1 + model/{player.go => player_stats.go} | 9 ++--- model/schema.go | 12 +++++-- model/steam_user.go | 15 +++++++-- model/user.go | 38 +++++++++++++++++++++ utils/utils.go | 1 + websocket/server.go | 6 +++- 14 files changed, 167 insertions(+), 56 deletions(-) rename controller/{login.go => auth.go} (85%) delete mode 100644 controller/player.go create mode 100644 controller/user.go rename model/{player.go => player_stats.go} (81%) create mode 100644 model/user.go diff --git a/config/store.go b/config/store.go index b9c7964..c96b3d5 100644 --- a/config/store.go +++ b/config/store.go @@ -7,19 +7,16 @@ import ( "github.com/gorilla/sessions" ) -var Store = sessions.NewCookieStore([]byte("test")) - -func SessionAlreadyExists(r *http.Request) bool { - session, _ := Store.Get(r, SESSION_NAME) - return !session.IsNew -} +var Store = sessions.NewCookieStore([]byte(SESSION_SECRET_KEY)) +// Creates the auth token func CreateSessionID(w http.ResponseWriter, r *http.Request, value string) error { session, _ := Store.Get(r, SESSION_NAME) session.Values["session-id"] = value return session.Save(r, w) } +// Removes the auth token func RemoveSessionID(w http.ResponseWriter, r *http.Request) { cookie := &http.Cookie{ Name: SESSION_NAME, @@ -30,7 +27,14 @@ func RemoveSessionID(w http.ResponseWriter, r *http.Request) { http.SetCookie(w, cookie) } +// Returns the auth token func GetSessionID(r *http.Request) string { session, _ := Store.Get(r, SESSION_NAME) return fmt.Sprintf("%s", session.Values["session-id"]) } + +// Returns true if the cookie session is set +func SessionAlreadyExists(r *http.Request) bool { + session, _ := Store.Get(r, SESSION_NAME) + return !session.IsNew +} diff --git a/controller/login.go b/controller/auth.go similarity index 85% rename from controller/login.go rename to controller/auth.go index cc12d02..e7e4e0d 100644 --- a/controller/login.go +++ b/controller/auth.go @@ -9,6 +9,7 @@ import ( "github.com/solovev/steam_go" ) +// GET /login - redirect to steam auth and validate user func Login(w http.ResponseWriter, r *http.Request) { opId := steam_go.NewOpenId(r) @@ -20,26 +21,27 @@ func Login(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, opId.AuthUrl(), http.StatusTemporaryRedirect) default: // login success - w.Header().Set("Content-Type", "application/json") user, err := opId.ValidateAndGetUser(config.STEAM_API_KEY) if err != nil { - utils.APIErrorRespond(w, utils.NewAPIError(http.StatusInternalServerError, "ValidateAndGetUser:"+err.Error())) + utils.APIErrorRespond(w, utils.NewAPIError(http.StatusInternalServerError, err.Error())) return } if err = CreateSteamUser(user); err != nil { - utils.APIErrorRespond(w, utils.NewAPIError(http.StatusInternalServerError, "CreateSteamUser:"+err.Error())) + utils.APIErrorRespond(w, utils.NewAPIError(http.StatusInternalServerError, err.Error())) return } config.CreateSessionID(w, r, user.SteamId) w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(user) + // TODO: redirect? } } +// GET /logout - Log out from current session func Logout(w http.ResponseWriter, r *http.Request) { config.RemoveSessionID(w, r) http.Redirect(w, r, "/", http.StatusTemporaryRedirect) diff --git a/controller/player.go b/controller/player.go deleted file mode 100644 index 6c9f643..0000000 --- a/controller/player.go +++ /dev/null @@ -1,23 +0,0 @@ -package controller - -import ( - "encoding/json" - "net/http" - - "github.com/gorilla/mux" - "github.com/robyzzz/csl-backend/model" - "github.com/robyzzz/csl-backend/utils" -) - -// GET /api/playerstats/{steamid} - returns {steamid}'s stats or 404 if not found -func GetPlayerStats(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - - player, err := model.GetPlayerStats(mux.Vars(r)["steamid"]) - if err != nil { - utils.APIErrorRespond(w, utils.NewAPIError(http.StatusNotFound, err.Error())) - return - } - - json.NewEncoder(w).Encode(player) -} diff --git a/controller/steam_user.go b/controller/steam_user.go index e517ccb..2f09675 100644 --- a/controller/steam_user.go +++ b/controller/steam_user.go @@ -11,8 +11,7 @@ import ( "github.com/solovev/steam_go" ) -// GET /api/player/{steamid} -// Returns {steamid}'s steam data or 404 if not found +// GET /api/steam/{steamid} - Returns player's steam data from db or 404 if not found func GetSteamUser(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -25,13 +24,13 @@ func GetSteamUser(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(player) } -// Not an API function +// Not an API function! // Called when user logs in with steam func CreateSteamUser(user *steam_go.PlayerSummaries) error { return model.CreateSteamUser(utils.PlayerSummariesToSteamUser(user)) } -// Not an API function +// Not an API function! // Called when we want to update our logged user steam data using his session ID // Updates steam data or internal server error if smt bad happened func UpdateSteamUser(w http.ResponseWriter, r *http.Request) { diff --git a/controller/user.go b/controller/user.go new file mode 100644 index 0000000..a0331d6 --- /dev/null +++ b/controller/user.go @@ -0,0 +1,50 @@ +package controller + +import ( + "encoding/json" + "net/http" + + "github.com/gorilla/mux" + "github.com/robyzzz/csl-backend/config" + "github.com/robyzzz/csl-backend/model" + "github.com/robyzzz/csl-backend/utils" +) + +// GET /api/users/ - returns all users data +func UserIndex(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + + users, err := model.UserIndex() + if err != nil { + utils.APIErrorRespond(w, utils.NewAPIError(http.StatusNotFound, err.Error())) + return + } + + json.NewEncoder(w).Encode(users) +} + +// GET /api/user/{steamid} - returns player's data by steamid +func GetUser(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + + user, err := model.GetUser(mux.Vars(r)["steamid"]) + if err != nil { + utils.APIErrorRespond(w, utils.NewAPIError(http.StatusNotFound, err.Error())) + return + } + + json.NewEncoder(w).Encode(user) +} + +// GET /profile - returns user stats if authenticated +func Profile(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + + user, err := model.GetUser(config.GetSessionID(r)) + if err != nil { + utils.APIErrorRespond(w, utils.NewAPIError(http.StatusNotFound, err.Error())) + return + } + + json.NewEncoder(w).Encode(user) +} diff --git a/main.go b/main.go index 2dfe9d2..f45e48c 100644 --- a/main.go +++ b/main.go @@ -29,26 +29,29 @@ func main() { func setupRouter() { router = mux.NewRouter() router.HandleFunc("/", controller.Home) - router.HandleFunc("/logout", controller.Logout) - router.Handle("/login", middleware.IsLogged(controller.Login)) + + // auth + router.Handle("/logout", middleware.IsAuthenticated(controller.Logout)) + router.Handle("/login", middleware.BeforeLogin(controller.Login)) // steam user - router.HandleFunc("/api/player/{steamid}", controller.GetSteamUser).Methods("GET") + router.HandleFunc("/api/steam/{steamid}", controller.GetSteamUser).Methods("GET") - // player stats - router.HandleFunc("/api/playerstats/{steamid}", controller.GetPlayerStats).Methods("GET") + // user (all data) + router.HandleFunc("/api/users", controller.UserIndex).Methods("GET") + router.HandleFunc("/api/user/{steamid}", controller.GetUser).Methods("GET") + router.Handle("/profile", middleware.IsAuthenticated(controller.Profile)).Methods("GET") // hub/lobby websocket.Hub = websocket.NewHub() + router.HandleFunc("/api/rooms", websocket.HandleHub) //TODO: apply isauth middleware after everything is done go websocket.RunHub() - router.HandleFunc("/api/rooms", websocket.HandleHub) // rooms (TODO: no hardcode range) for _, uid := range []int{1, 2, 3, 4, 5} { newRoom := websocket.NewRoom(uid, 3) websocket.AddRoomToHub(newRoom) - router.HandleFunc("/api/room/"+fmt.Sprint(uid), newRoom.HandleRoom) + router.HandleFunc("/api/room/"+fmt.Sprint(uid), newRoom.HandleRoom) //TODO: apply isauth middleware after everything is done go newRoom.Run() } - } diff --git a/middleware/auth.go b/middleware/auth.go index 4d70cf4..45c5afa 100644 --- a/middleware/auth.go +++ b/middleware/auth.go @@ -7,10 +7,22 @@ import ( "github.com/robyzzz/csl-backend/controller" ) -// Used to update steam user data when acessing a route or to let them log in +// Used to let client access only routes that need him to be logged in +func IsAuthenticated(h func(w http.ResponseWriter, r *http.Request)) http.Handler { + next := http.HandlerFunc(h) + return http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + if config.SessionAlreadyExists(r) { + next.ServeHTTP(w, r) + } else { + http.Redirect(w, r, "/404", http.StatusTemporaryRedirect) + } + }) +} + +// Used to update steam user data when acessing /login // If user is already logged in, we update, else we redirect to login page -// Usage: /login -func IsLogged(h func(w http.ResponseWriter, r *http.Request)) http.Handler { +func BeforeLogin(h func(w http.ResponseWriter, r *http.Request)) http.Handler { next := http.HandlerFunc(h) return http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { diff --git a/model/db.go b/model/db.go index cb5ff6a..8e2e085 100644 --- a/model/db.go +++ b/model/db.go @@ -13,6 +13,7 @@ import ( var db *sqlx.DB var err error +// Connects to database func Connect() { db, err = sqlx.Open("postgres", connToString()) if err != nil { diff --git a/model/player.go b/model/player_stats.go similarity index 81% rename from model/player.go rename to model/player_stats.go index 7865c66..e763ff5 100644 --- a/model/player.go +++ b/model/player_stats.go @@ -1,5 +1,6 @@ package model +// PlayerStats represents game data type PlayerStats struct { ID uint64 `json:"id"` Player_SteamID string `json:"player_steamid"` @@ -24,8 +25,8 @@ type PlayerStats struct { Mvp int `json:"mvp"` } -func GetPlayerStats(steamID string) (PlayerStats, error) { - user := PlayerStats{} - err := db.Get(&user, "SELECT * FROM player_stats WHERE player_steamid = $1;", steamID) - return user, err +func CreatePlayer(steamid string) error { + query := `INSERT INTO player_stats(player_steamid, map_id) VALUES ($1,1);` + _, err = db.Exec(query, steamid) + return err } diff --git a/model/schema.go b/model/schema.go index 580cb04..6e9adc9 100644 --- a/model/schema.go +++ b/model/schema.go @@ -65,7 +65,15 @@ INSERT INTO steam_user(steamid, personaname, lastlogoff, profileurl, avatar, ava INSERT INTO map(name) VALUES ('xdream'); -INSERT INTO player_stats(player_steamid, map_id) VALUES ('76561198226912040',1); +INSERT INTO player_stats(player_steamid, map_id) VALUES ('1337steamidyes',1); + +INSERT INTO player_stats(player_steamid, map_id, kills, deaths, assists, shots, + hits, damage, first_blood, aces, headshots, no_scope, count, playtime, match_win) + VALUES ('76561198226912040', 2, 7, 2, 7, 7, 7, 1, 7, 5, 7, 7, 2, 7, 7); +INSERT INTO player_stats(player_steamid, map_id, kills, deaths, assists, shots, + hits, damage, first_blood, aces, headshots, no_scope, count, playtime, match_win) + VALUES ('76561198226912040', 3, 1, 2, 3, 6, 17, 1, 72, 5, 17, 7, 12, 0, 7); + ` -// TODO: add fkeys (map id, steamid, ..) +// TODO: add fkey (steamid -> player_steamid ?, ..) diff --git a/model/steam_user.go b/model/steam_user.go index 6ae35e1..f9590c6 100644 --- a/model/steam_user.go +++ b/model/steam_user.go @@ -4,6 +4,7 @@ import ( "log" ) +// SteamUser represents steam user's relevant data type SteamUser struct { ID uint64 `json:"id"` SteamID string `json:"steamid"` @@ -21,12 +22,14 @@ type SteamUser struct { Updated_At string `json:"updated_at"` } -func GetSteamUser(steamID string) (SteamUser, error) { +// Returns a user from database by steamid +func GetSteamUser(steamid string) (SteamUser, error) { user := SteamUser{} - err := db.Get(&user, "SELECT * FROM steam_user WHERE steamid = $1;", steamID) + err := db.Get(&user, "SELECT * FROM steam_user WHERE steamid = $1;", steamid) return user, err } +// Creates a new user on the database func CreateSteamUser(user SteamUser) error { userExists, err := DoesSteamUserExist(user.SteamID) if err != nil { @@ -49,9 +52,16 @@ func CreateSteamUser(user SteamUser) error { return err } + err = CreatePlayer(user.SteamID) + if err != nil { + log.Printf("Error creating Player @CreateSteamUser: %s\n", err.Error()) + return err + } + return nil } +// Updates user from the database func UpdateSteamUser(user SteamUser) error { query := `UPDATE steam_user SET personaname = $1::text, lastlogoff = $2, profileurl = $3::text, avatar = $4::text, avatarmedium = $5::text, avatarfull = $6::text, realname = $7::text, @@ -70,6 +80,7 @@ func UpdateSteamUser(user SteamUser) error { return nil } +// Returns true if user by steamid already exists func DoesSteamUserExist(steamid string) (bool, error) { var count int err := db.QueryRow("SELECT COUNT(*) FROM steam_user WHERE steamid = '" + steamid + "';").Scan(&count) diff --git a/model/user.go b/model/user.go new file mode 100644 index 0000000..6ca0b9e --- /dev/null +++ b/model/user.go @@ -0,0 +1,38 @@ +package model + +// User represents all data(player_stats + steam_user) +// TODO: +type User struct { + ID uint64 `json:"id"` + SteamID string `json:"steamid"` + Kills int `json:"kills"` + Deaths int `json:"deaths"` + Assists int `json:"assists"` + Shots int `json:"shots"` + Hits int `json:"hits"` + Damage int `json:"damage"` + First_Blood int `json:"first_blood"` + Aces int `json:"aces"` + Headshots int `json:"headshots"` + No_Scope int `json:"no_scope"` + Count int `json:"count"` + Playtime int `json:"playtime"` + Match_Win int `json:"match_win"` + Match_Lose int `json:"match_lose"` + Match_Draw int `json:"match_draw"` + Rounds_Won int `json:"rounds_won"` + Rounds_Lost int `json:"rounds_lost"` + Mvp int `json:"mvp"` +} + +// Returns all players stats from db +func UserIndex() ([]User, error) { + // TODO: get player_stats joined with steam_user stuff + return []User{}, nil +} + +// Returns player's data from db by steamid +func GetUser(steamid string) (User, error) { + // TODO: get player_stats joined with steam_user stuff + return User{}, err +} diff --git a/utils/utils.go b/utils/utils.go index e154908..bfb01a8 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -9,6 +9,7 @@ import ( "github.com/solovev/steam_go" ) +// ErrorResponse represents a error object which we can send when API fails type ErrorResponse struct { Code int ErrorMsg string diff --git a/websocket/server.go b/websocket/server.go index 6c1f126..30499c9 100644 --- a/websocket/server.go +++ b/websocket/server.go @@ -1,6 +1,7 @@ package websocket import ( + "fmt" "log" "net/http" @@ -24,7 +25,6 @@ var upgrader = &websocket.Upgrader{ // /api/rooms - handle the room lobby (aKa hub) func HandleHub(w http.ResponseWriter, r *http.Request) { - // TODO: change this to bearer auth? steamid := r.URL.Query().Get("steamid") if len(steamid) == 0 { utils.APIErrorRespond(w, utils.NewAPIError(http.StatusNotFound, "Invalid steamid")) @@ -42,6 +42,10 @@ func HandleHub(w http.ResponseWriter, r *http.Request) { return } + for _, c := range r.Cookies() { + fmt.Println(c) + } + log.Println("Someone connected to /rooms") client := newClient(steamid, socket, nil)