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