about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorEmile <git@emile.space>2024-11-08 11:39:04 +0100
committerEmile <git@emile.space>2024-11-08 11:39:04 +0100
commitc685cc8f25adc5b2b72e4bda185fef7ec8dd6592 (patch)
treecf7e9d91eb5219349aec81b29f043b5d72d08af8 /src
parent8ffae39cd303d487cf20177fab2f7b9aa29f1d77 (diff)
push all, yolo
Diffstat (limited to 'src')
-rw-r--r--src/battle.go177
-rw-r--r--src/db.go6
-rw-r--r--src/main.go4
-rw-r--r--src/user.go16
4 files changed, 182 insertions, 21 deletions
diff --git a/src/battle.go b/src/battle.go
index e30cebd..01e9f8d 100644
--- a/src/battle.go
+++ b/src/battle.go
@@ -23,6 +23,7 @@ type Battle struct {
 	Archs     []Arch
 	Bits      []Bit
 	RawOutput string
+	MaxRounds int
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -32,8 +33,8 @@ func BattleGetAll() ([]Battle, error) {
 	return globalState.GetAllBattles()
 }
 
-func BattleCreate(name string, public bool) (int, error) {
-	return globalState.InsertBattle(Battle{Name: name, Public: public})
+func BattleCreate(name string, public bool, owner User) (int, error) {
+	return globalState.InsertBattle(Battle{Name: name, Public: public}, owner)
 }
 
 func BattleLinkBot(botid int, battleid int) error {
@@ -64,10 +65,15 @@ func BattleSaveRawOutput(battleid int, rawOutput string) error {
 	return globalState.UpdateBattleRawOutput(battleid, rawOutput)
 }
 
+func BattleDeleteID(battleid int) error {
+	return globalState.DeleteBattleByID(battleid)
+}
+
 //////////////////////////////////////////////////////////////////////////////
 // DATABASE
 
-func (s *State) InsertBattle(battle Battle) (int, error) {
+func (s *State) InsertBattle(battle Battle, owner User) (int, error) {
+	// create the battle
 	res, err := s.db.Exec("INSERT INTO battles VALUES(NULL,?,?,?);", time.Now(), battle.Name, battle.Public)
 	if err != nil {
 		log.Println(err)
@@ -79,6 +85,14 @@ func (s *State) InsertBattle(battle Battle) (int, error) {
 		log.Println(err)
 		return -1, err
 	}
+
+	// insert the owner into the battle_owner rel
+	_, err = s.db.Exec("INSERT INTO owner_battle_rel VALUES (?, ?)", owner.ID, battle.ID)
+	if err != nil {
+		log.Println(err)
+		return -1, err
+	}
+
 	return int(id), nil
 }
 
@@ -219,6 +233,7 @@ func (s *State) GetBattleByIdDeep(id int) (Battle, error) {
 	var battlename string
 	var battlepublic bool
 	var battlerawoutput string
+	var battlemaxrounds int
 
 	var botids string
 	var botnames string
@@ -239,10 +254,15 @@ func (s *State) GetBattleByIdDeep(id int) (Battle, error) {
 	// TODO(emile): go deeper! we could fetch battle -> bot -> arch (so fetching the linked arch
 	//              for the given bot)
 
+	// COALESCE is used to set default values
+	// TODO(emile): do fancy migrations instead of the COALESCE stuff for setting defaults if
+	// no value is set beforehand
+
 	err := s.db.QueryRow(`
 	SELECT DISTINCT
 		ba.id, ba.name, ba.public, 
 		COALESCE(ba.raw_output, ""),
+		COALESCE(ba.max_rounds, 100),
 		COALESCE(group_concat(DISTINCT bb.bot_id), ""),
 		COALESCE(group_concat(DISTINCT bo.name), ""),
 		COALESCE(group_concat(DISTINCT ub.user_id), ""),
@@ -267,7 +287,7 @@ func (s *State) GetBattleByIdDeep(id int) (Battle, error) {
 
 	WHERE ba.id=?
 	GROUP BY ba.id;
-	`, id).Scan(&battleid, &battlename, &battlepublic, &battlerawoutput, &botids, &botnames, &userids, &usernames, &archids, &archnames, &bitids, &bitnames)
+	`, id).Scan(&battleid, &battlename, &battlepublic, &battlerawoutput, &battlemaxrounds, &botids, &botnames, &userids, &usernames, &archids, &archnames, &bitids, &bitnames)
 	if err != nil {
 		log.Println(err)
 		return Battle{}, err
@@ -361,6 +381,7 @@ func (s *State) GetBattleByIdDeep(id int) (Battle, error) {
 		Archs:     archs,
 		Bits:      bits,
 		RawOutput: battlerawoutput,
+		MaxRounds: battlemaxrounds,
 	}, nil
 }
 
@@ -373,6 +394,35 @@ func (s *State) UpdateBattleRawOutput(battleid int, rawOutput string) error {
 	return nil
 }
 
+// This deletes a battle and all links to users, bots, architectures and bits
+func (s *State) DeleteBattleByID(battleid int) error {
+	_, err := s.db.Exec(`
+	DELETE FROM battles WHERE battleid = ?;
+
+	DELETE FROM user_battle_rel WHERE battleid = ?;
+	DELETE FROM bot_battle_rel WHERE battleid = ?;
+	DELETE FROM arch_battle_rel WHERE battleid = ?;
+	DELETE FROM bit_battle_rel WHERE battleid = ?;
+	`, battleid, battleid, battleid, battleid, battleid)
+
+	if err != nil {
+		log.Println(err)
+		return err
+	}
+
+	_, err = s.db.Exec(`
+	DELETE FROM battles
+	WHERE battleid = ?
+	`, battleid)
+
+	if err != nil {
+		log.Println(err)
+		return err
+	}
+
+	return nil
+}
+
 //////////////////////////////////////////////////////////////////////////////
 // HTTP
 
@@ -498,6 +548,20 @@ func battleNewHandler(w http.ResponseWriter, r *http.Request) {
 		t.ExecuteTemplate(w, "battleNew", data)
 
 	case "POST":
+		data := map[string]interface{}{}
+
+		session, _ := globalState.sessions.Get(r, "session")
+		username := session.Values["username"].(string)
+
+		// get data needed
+		user, err := UserGetUserFromUsername(username)
+		if err != nil {
+			log.Println(err)
+			data["err"] = "Could not fetch the user"
+		} else {
+			data["user"] = user
+		}
+
 		// parse the post parameters
 		r.ParseForm()
 		name := r.Form.Get("name")
@@ -538,7 +602,7 @@ func battleNewHandler(w http.ResponseWriter, r *http.Request) {
 		if name != "" {
 			// create the battle itself
 			log.Println("Creating battle")
-			battleid, err := BattleCreate(name, public)
+			battleid, err := BattleCreate(name, public, user)
 			if err != nil {
 				log.Println(err)
 				msg := "ERROR: Could not create due to internal reasons"
@@ -631,6 +695,20 @@ func battleQuickHandler(w http.ResponseWriter, r *http.Request) {
 		t.ExecuteTemplate(w, "battleQuick", data)
 
 	case "POST":
+		data := map[string]interface{}{}
+
+		session, _ := globalState.sessions.Get(r, "session")
+		username := session.Values["username"].(string)
+
+		// get data needed
+		user, err := UserGetUserFromUsername(username)
+		if err != nil {
+			log.Println(err)
+			data["err"] = "Could not fetch the user"
+		} else {
+			data["user"] = user
+		}
+
 		// parse the post parameters
 		r.ParseForm()
 
@@ -658,7 +736,7 @@ func battleQuickHandler(w http.ResponseWriter, r *http.Request) {
 
 		// create the battle itself
 		log.Println("Creating battle")
-		battleid, err := BattleCreate("quick", public)
+		battleid, err := BattleCreate("quick", public, user)
 		if err != nil {
 			log.Println(err)
 			msg := "ERROR: Could not create due to internal reasons"
@@ -872,6 +950,20 @@ func battleSingleHandler(w http.ResponseWriter, r *http.Request) {
 
 		// at this point, we're sure the user is allowed to edit the battle
 
+		data := map[string]interface{}{}
+
+		session, _ := globalState.sessions.Get(r, "session")
+		username := session.Values["username"].(string)
+
+		// get data needed
+		user, err := UserGetUserFromUsername(username)
+		if err != nil {
+			log.Println(err)
+			data["err"] = "Could not fetch the user"
+		} else {
+			data["user"] = user
+		}
+
 		r.ParseForm()
 
 		log.Println("r.Form: ", r.Form)
@@ -919,7 +1011,7 @@ func battleSingleHandler(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 
-		new_battle := Battle{int(battleid), form_name, []Bot{}, []User{}, public, []Arch{}, []Bit{}, ""}
+		new_battle := Battle{int(battleid), form_name, []Bot{}, []User{user}, public, []Arch{}, []Bit{}, "", 100}
 
 		log.Println("Updating battle...")
 		err = BattleUpdate(new_battle)
@@ -1220,12 +1312,65 @@ func battleRunHandler(w http.ResponseWriter, r *http.Request) {
 
 // delete a battle
 // TODO(emile): finish implementing the deletion of battles
-//  func battleDeleteHandler(w http.ResponseWriter, r *http.Request) {
-//  	switch r.Method {
-//  	case "DELETE":
-
-//  		http.Redirect(w, r, fmt.Sprintf("/battle?res=%s", battleid, msg), http.StatusSeeOther)
-//  	default:
-//  		http.Redirect(w, r, "/", http.StatusMethodNotAllowed)
-//  	}
-//  }
+func battleDeleteHandler(w http.ResponseWriter, r *http.Request) {
+	vars := mux.Vars(r)
+	battleid, err := strconv.Atoi(vars["id"])
+	if err != nil {
+		w.WriteHeader(http.StatusInternalServerError)
+		w.Write([]byte("500 - Invalid battle id"))
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+
+	redir_target := fmt.Sprintf("/battle/%d?res=%%s", battleid)
+
+	switch r.Method {
+	case "POST": // can't send a DELETE with pure HTML...
+
+		// get the current user
+		session, _ := globalState.sessions.Get(r, "session")
+		username := session.Values["username"]
+		if username == nil {
+			http.Redirect(w, r, "/login", http.StatusSeeOther)
+			return
+		}
+		viewer, err := UserGetUserFromUsername(username.(string))
+		if err != nil {
+			log_and_redir_with_msg(w, r, err, redir_target, "Could not get the id for your username")
+			return
+		}
+
+		// get the battle
+		battle, err := BattleGetByIdDeep(int(battleid))
+		if err != nil {
+			log_and_redir_with_msg(w, r, err, redir_target, "Could not get the battle given the id provided")
+			return
+		}
+
+		battle_owned_by_requesting_user := false
+		for _, owner := range battle.Owners {
+
+			// if the requesting users id is equal to an owners id, we're allowed to delete
+			// the battle
+			if viewer.ID == owner.ID {
+				battle_owned_by_requesting_user = true
+				break
+			}
+		}
+
+		// check that the user that created the battle is the current user, if not, return
+		if battle_owned_by_requesting_user == false {
+			msg := "You aren't in the owners list of the battle, so you can't delete this battle"
+			log_and_redir_with_msg(w, r, err, redir_target, msg)
+			return
+		}
+
+		BattleDeleteID(battleid)
+
+		msg := "Successfully deleted the battle"
+		http.Redirect(w, r, fmt.Sprintf("/battle/%d?res=%s", battleid, msg), http.StatusSeeOther)
+	default:
+		log.Println("expected POST, got ", r.Method)
+		http.Redirect(w, r, "/", http.StatusMethodNotAllowed)
+	}
+}
diff --git a/src/db.go b/src/db.go
index 426ac52..42dc2d4 100644
--- a/src/db.go
+++ b/src/db.go
@@ -24,6 +24,7 @@ CREATE TABLE IF NOT EXISTS battles (
 	name TEXT,
 	public BOOLEAN,
 	raw_output TEXT
+	max_rounds INTEGER
 );
 CREATE TABLE IF NOT EXISTS archs (
 	id INTEGER NOT NULL PRIMARY KEY,
@@ -90,6 +91,11 @@ CREATE TABLE IF NOT EXISTS user_battle_rel (
 	battle_id INTEGER,
 	PRIMARY KEY(user_id, battle_id)
 );
+CREATE TABLE IF NOT EXISTS owner_battle_rel (
+	user_id INTEGER,
+	battle_id INTEGER,
+	PRIMARY KEY(user_id, battle_id)
+);
 CREATE TABLE IF NOT EXISTS bot_battle_rel (
 	bot_id INTEGER,
 	battle_id INTEGER,
diff --git a/src/main.go b/src/main.go
index 20c245a..c789cf8 100644
--- a/src/main.go
+++ b/src/main.go
@@ -30,7 +30,7 @@ func initFlags() {
 
 	flag.StringVar(&logFilePath, "logfilepath", "./server.log", "The path to the log file")
 	flag.StringVar(&databasePath, "databasepath", "./main.db", "The path to the main database")
-	flag.StringVar(&sessiondbPath, "sessiondbpath", "./sesions.db", "The path to the session database")
+	flag.StringVar(&sessiondbPath, "sessiondbpath", "./sessions.db", "The path to the session database")
 	flag.StringVar(&templatesPath, "templates", "./templates", "The path to the templates used")
 }
 
@@ -92,7 +92,7 @@ func main() {
 	auth_needed.HandleFunc("/battle/quick", battleQuickHandler)
 	auth_needed.HandleFunc("/battle/{id}/submit", battleSubmitHandler)
 	auth_needed.HandleFunc("/battle/{id}/run", battleRunHandler)
-	//  auth_needed.HandleFunc("/battle/{id}/delete", battleDeleteHandler)
+	auth_needed.HandleFunc("/battle/{id}/delete", battleDeleteHandler)
 
 	log.Printf("[i] HTTP Server running on %s:%d\n", host, port)
 	log.Fatal(http.ListenAndServe(fmt.Sprintf("%s:%d", host, port), r))
diff --git a/src/user.go b/src/user.go
index d55c9d8..1fd9358 100644
--- a/src/user.go
+++ b/src/user.go
@@ -119,7 +119,9 @@ func (s *State) UpdateUserUsername(id int, new_username string) error {
 
 // Links the given bot to the given user in the user_bot_rel table
 func (s *State) LinkUserBot(username string, botid int) error {
-	_, err := s.db.Exec("INSERT INTO user_bot_rel VALUES ((SELECT id FROM users WHERE name=?), ?)", username, botid)
+	_, err := s.db.Exec(`
+		INSERT INTO user_bot_rel
+		VALUES ((SELECT id FROM users WHERE name=?), ?)`, username, botid)
 	if err != nil {
 		return err
 	} else {
@@ -153,7 +155,11 @@ func (s *State) GetUserFromUsername(username string) (User, error) {
 // Returns the bots belonging to the given user
 // TODO(emile): Also fetch the bits and the archs for displaying in the single battle page. In order to do so, join in both those tables
 func (s *State) GetUserBotsUsername(username string) ([]Bot, error) {
-	rows, err := s.db.Query("SELECT id, name, source FROM bots b LEFT JOIN user_bot_rel ub ON ub.bot_id = b.id WHERE ub.user_id=(SELECT id FROM users WHERE name=?)", username)
+	rows, err := s.db.Query(`
+		SELECT id, name, source
+		FROM bots b
+		LEFT JOIN user_bot_rel ub ON ub.bot_id = b.id
+		WHERE ub.user_id=(SELECT id FROM users WHERE name=?)`, username)
 	defer rows.Close()
 	if err != nil {
 		return nil, err
@@ -175,7 +181,11 @@ func (s *State) GetUserBotsUsername(username string) ([]Bot, error) {
 
 // Returns the bots belonging to the given user
 func (s *State) GetUserBotsId(id int) ([]Bot, error) {
-	rows, err := s.db.Query("SELECT id, name, source FROM bots b LEFT JOIN user_bot_rel ub ON ub.bot_id = b.id WHERE ub.user_id=?", id)
+	rows, err := s.db.Query(`
+		SELECT id, name, source
+		FROM bots b
+		LEFT JOIN user_bot_rel ub ON ub.bot_id = b.id
+		WHERE ub.user_id=?`, id)
 	defer rows.Close()
 	if err != nil {
 		return nil, err