From 84e843b59672ef52bf1ca2c5b0d82d3e1a1824ef Mon Sep 17 00:00:00 2001 From: Alex Constantin-Gomez <ac3419@ic.ac.uk> Date: Sun, 9 Feb 2020 01:15:28 +0000 Subject: [PATCH] added database, not working yet --- main.go | 271 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 144 insertions(+), 127 deletions(-) diff --git a/main.go b/main.go index cc4b347..3f23dc9 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "database/sql" "encoding/json" "fmt" "io/ioutil" @@ -10,6 +11,7 @@ import ( "sync" _ "github.com/mattn/go-sqlite3" + "golang.org/x/crypto/bcrypt" ) /******************** DATA STRUCTS ***********************/ @@ -43,7 +45,7 @@ var ( idCounter int idCounterMutex sync.RWMutex - // database *sql.DB + database *sql.DB ) /************** MAIN FUNCTION **********************/ @@ -52,14 +54,19 @@ func main() { // Initialise global variables gameData = make(map[int]*Game) - // database, err := sql.Open("sqlite3", "./database.db") - // if err != nil { - // log.Fatal("could not connect to database") - // } - // defer database.Close() + connStr := "dbname=bigtactoe sslmode=require" + database, err := sql.Open("postgres", connStr) + if err != nil { + log.Fatal("could not connect to database") + } + defer database.Close() - // statement, _ := database.Prepare("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, username TEXT, password TEXT, wins INTEGER)") - // statement.Exec() + statement, _ := database.Prepare("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, username TEXT UNIQUE, password TEXT, wins INTEGER)") + statement.Exec() + + // test query + database.QueryRow("INSERT INTO users(username, password, wins) VALUES($1, $2, $3);", + "alex123", "qwerty", 0) http.HandleFunc("/", hello) @@ -83,10 +90,15 @@ func main() { /************************ HANDLERS ************************/ func hello(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Big Tac Toe") + fmt.Fprintf(w, "Welcome to Big Tac Toe") } func move(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + fmt.Fprintf(w, "Invalid request method") + return + } + jsonData := readJsonFromRequest(r) var move Move @@ -113,6 +125,12 @@ func move(w http.ResponseWriter, r *http.Request) { // Creates a game with one player func createGame(w http.ResponseWriter, r *http.Request) { + + if r.Method != http.MethodPost { + fmt.Fprintf(w, "Invalid request method") + return + } + jsonData := readJsonFromRequest(r) var playerData struct { @@ -138,6 +156,12 @@ func createGame(w http.ResponseWriter, r *http.Request) { // Adds a 2nd player to a game, given the game id func joinGame(w http.ResponseWriter, r *http.Request) { + + if r.Method != http.MethodPost { + fmt.Fprintf(w, "Invalid request method") + return + } + jsonData := readJsonFromRequest(r) var join struct { @@ -164,6 +188,12 @@ func joinGame(w http.ResponseWriter, r *http.Request) { // Returns if the 2nd player has joined the game yet func checkJoinGame(w http.ResponseWriter, r *http.Request) { + + if r.Method != http.MethodPost { + fmt.Fprintf(w, "Invalid request method") + return + } + jsonData := readJsonFromRequest(r) var gameID struct { @@ -188,124 +218,111 @@ func checkJoinGame(w http.ResponseWriter, r *http.Request) { } // /************** User authentication and other ****************/ -// func createUser(w http.ResponseWriter, r *http.Request) { -// jsonData := readJsonFromRequest(r) - -// var account struct { -// Username string `json:"username"` -// Password string `json:"password"` -// } -// err := json.Unmarshal(jsonData, &account) -// if err != nil { -// log.Println("could not decode json data") -// } - -// // response will be an error message and success boolean -// response, ok := newAccount(account.Username, account.Password) -// if !ok { -// fmt.Fprintf(w, response) -// } else { -// fmt.Fprintf(w, account.Username) // if success, return the username -// } -// } - -// func login(w http.ResponseWriter, r *http.Request) { -// jsonData := readJsonFromRequest(r) - -// var account struct { -// Username string `json:"username"` -// Password string `json:"password"` -// } -// err := json.Unmarshal(jsonData, &account) -// if err != nil { -// log.Println("could not decode json data") -// } - -// // response will be the new user's ID if successful, otherwise, will be other message -// authenticated := authenticate(account.Username, account.Password) -// if authenticated { -// fmt.Fprintf(w, "logged in") -// } else { -// fmt.Fprintf(w, "wrong password") -// } -// } - -// // returns a map[username]wins as json -// func leaderboard(w http.ResponseWriter, r *http.Request) { -// usernames, ok := getLeaderboard() -// if !ok { -// fmt.Fprintf(w, "Could not get leaderboard") -// return -// } - -// jsonString, err := json.Marshal(usernames) -// if err != nil { -// log.Println("could not marshal leaderboard usernames into json") -// } - -// w.Header().Set("Content-Type", "application/json") -// w.WriteHeader(http.StatusCreated) -// w.Write(jsonString) -// } - -// /************** Database operations ****************/ - -// // Inserts new account into users table, returns true or false success and message -// func newAccount(username string, password string) (string, bool) { -// hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) -// if err != nil { -// return "Internal server error occurred while hashing your password.", false -// } -// statement, _ := database.Prepare("INSERT INTO users (username, password, wins) VALUES (?, ?, ?)") -// defer statement.Close() -// statement.Exec(username, hashedPassword, 0) -// if err != nil { -// return "That username has been taken", false -// } - -// return "", true -// } - -// // Authenticates a user, used to login. Returns true if successful -// func authenticate(username string, password string) bool { -// var hashed []byte -// // err := database.QueryRow("SELECT password FROM users WHERE username=$1;", -// // username).Scan(&hashed) -// // if err != nil { -// // return false -// // } - -// statement, err := database.Prepare("select password from users where username = ?") -// if err != nil { -// log.Fatal(err) -// } -// defer statement.Close() -// err = statement.QueryRow(username).Scan(&hashed) -// if err != nil { -// log.Fatal(err) -// } - -// err = bcrypt.CompareHashAndPassword(hashed, []byte(password)) -// if err != nil { -// return false -// } -// return true -// } - -// func getLeaderboard() (map[string]int, bool) { -// rows, err := database.Query("SELECT username, wins FROM users;") -// if err != nil { -// return nil, false -// } -// var usernames map[string]int -// for rows.Next() { -// var username string -// var wins int64 -// rows.Scan(&username, &wins) -// usernames[username] = int(wins) -// } -// return usernames, true -// } +func createUser(w http.ResponseWriter, r *http.Request) { + jsonData := readJsonFromRequest(r) + + var account struct { + Username string `json:"username"` + Password string `json:"password"` + } + err := json.Unmarshal(jsonData, &account) + if err != nil { + log.Println("could not decode json data") + } + + // response will be an error message and success boolean + response, ok := newAccount(account.Username, account.Password) + if !ok { + fmt.Fprintf(w, response) + } else { + fmt.Fprintf(w, account.Username) // if success, return the username + } +} + +func login(w http.ResponseWriter, r *http.Request) { + jsonData := readJsonFromRequest(r) + + var account struct { + Username string `json:"username"` + Password string `json:"password"` + } + err := json.Unmarshal(jsonData, &account) + if err != nil { + log.Println("could not decode json data") + } + + // response will be the new user's ID if successful, otherwise, will be other message + authenticated := authenticate(account.Username, account.Password) + if authenticated { + fmt.Fprintf(w, "logged in") + } else { + fmt.Fprintf(w, "wrong password") + } +} + +// returns a map[username]wins as json +func leaderboard(w http.ResponseWriter, r *http.Request) { + usernames, ok := getLeaderboard() + if !ok { + fmt.Fprintf(w, "Could not get leaderboard") + return + } + + jsonString, err := json.Marshal(usernames) + if err != nil { + log.Println("could not marshal leaderboard usernames into json") + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + w.Write(jsonString) +} + +/************** Database operations ****************/ + +// Inserts new account into users table, returns true or false success and message +func newAccount(username string, password string) (string, bool) { + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return "Internal server error occurred while hashing your password.", false + } + var userID string + err = database.QueryRow("INSERT INTO users(username, password, wins) VALUES($1, $2, $3) RETURNING id;", + username, hashedPassword, 0).Scan(&userID) + if err != nil { + return "That username has been taken", false + } + + return "", true +} + +// Authenticates a user, used to login. Returns true if successful +func authenticate(username string, password string) bool { + var hashed []byte + database.QueryRow("SELECT password FROM users WHERE username=$1;", + username).Scan(&hashed) + + err := bcrypt.CompareHashAndPassword(hashed, []byte(password)) + if err != nil { + return false + } + return true +} + +func getLeaderboard() (map[string]int, bool) { + rows, err := database.Query("SELECT username, wins FROM users;") + if err != nil { + return nil, false + } + var usernames map[string]int + for rows.Next() { + var username string + var wins int64 + rows.Scan(&username, &wins) + usernames[username] = int(wins) + } + return usernames, true +} /************** Helper functions *******************/ -- GitLab