package cmd import ( "crypto/sha256" "database/sql" "encoding/hex" "fmt" "log" "net/http" "strconv" "github.com/gin-gonic/gin" _ "github.com/lib/pq" "github.com/spf13/viper" ) var ( ListenPort = "8080" db *sql.DB DB_Host string DB_Port string DB_User string DB_Pass string DB_Name string ) func getDBInfo() error { dbhost := viper.GetString("Server.DB_Host") if dbhost == "" { e := fmt.Errorf("No database IP address present in config file!\n") return e } else { DB_Host = dbhost } dbport := viper.GetString("Server.DB_Port") if dbport == "" { e := fmt.Errorf("No database port present in config file!\n") return e } else { DB_Port = dbport } dbuser := viper.GetString("Server.DB_User") if dbuser == "" { e := fmt.Errorf("No database username present in config file!\n") return e } else { DB_User = dbuser } dbpass := viper.GetString("Server.DB_Pass") if dbpass == "" { e := fmt.Errorf("No database password present in config file!\n") return e } else { DB_Pass = dbpass } dbname := viper.GetString("Server.DB_Name") if dbname == "" { e := fmt.Errorf("No database name present in config file!\n") return e } else { DB_Name = dbname } return nil } func openDB() error { psqlconn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", DB_Host, DB_Port, DB_User, DB_Pass, DB_Name) dba, err := sql.Open("postgres", psqlconn) if err != nil { return err } db = dba return nil } func setPort() { p := viper.GetString("Server.Port") if p != "" { ListenPort = p } } func server() { log.SetPrefix("[DRAHOOT] ") setPort() if err := getDBInfo(); err != nil { log.Fatalf("INVALID DB INFO: %v\nPlease refer to the example configuration file in the repo at https://git.bulgariu.xyz/raul/drahoot/src/branch/main/sample-config (default config path = ~/.config/drahoot/drahoot.toml)", err) } if err := openDB(); err != nil { log.Fatalf("Error happened trying to connect to database: %v", err) } gin.SetMode(gin.ReleaseMode) r := gin.Default() r.GET("/", helloWorld) // TODO: Have fun creating new endpoints r.GET("/api/user", getUsers) r.GET("/api/user/:userid", getUser) r.POST("/api/user", createUser) r.DELETE("/api/user/:userid", deleteUser) r.Run(":" + ListenPort) } func hashPW(plain string) string { hashedPW := sha256.New() hashedPW.Write([]byte(plain)) sha256hash := hex.EncodeToString(hashedPW.Sum(nil)) return sha256hash } func deleteUser(c *gin.Context) { id := c.Param("userid") dynStmt := `DELETE FROM usuarios WHERE id_usuario = $1` _, err := strconv.Atoi(id) if err != nil { e := fmt.Sprintf("Invalid identifier") c.IndentedJSON(http.StatusNotFound, setResponse(e, false)) return } _, err = db.Exec(dynStmt, id) if err != nil { e := fmt.Sprintf("Something went wrong trying to delete the user: %v", err) log.Println(e) c.IndentedJSON(http.StatusInternalServerError, setResponse(e, false)) return } e := fmt.Sprintf("User successfully deleted") c.IndentedJSON(http.StatusOK, setResponse(e, true)) } func createUser(c *gin.Context) { newuser := user{} if err := c.BindJSON(&newuser); err != nil { e := fmt.Sprintf("Something went wrong creating the user: %v", err) log.Println(e) c.IndentedJSON(http.StatusInternalServerError, setResponse(e, false)) return } secret := hashPW(newuser.Password) var dynStmt string if newuser.AccountType != "estudiante" && newuser.AccountType != "profesor" { if newuser.AccountType == "admin" { e := fmt.Sprintf("Nice try (https://xkcd.com/327/)") c.IndentedJSON(http.StatusTeapot, setResponse(e, false)) return } e := fmt.Sprintf("Invalid account type\n") c.IndentedJSON(http.StatusNotFound, setResponse(e, false)) return } dynStmt = `INSERT INTO usuarios(nombre, apellido1, apellido2, email, password, rol) values($1, $2, $3, $4, $5, $6)` _, err := db.Exec(dynStmt, newuser.Name, newuser.Surname1, newuser.Surname2, newuser.Email, secret, newuser.AccountType) if err != nil { e := fmt.Sprintf("Something went wrong trying to create the user: %v", err) log.Println(e) c.IndentedJSON(http.StatusInternalServerError, setResponse(e, false)) return } e := fmt.Sprintf("User %v has been created!", newuser.Name) c.IndentedJSON(http.StatusOK, setResponse(e, true)) } func setResponse(content any, success bool) response { msg := response{Contents: content, Success: success} return msg } func getUser(c *gin.Context) { id := c.Param("userid") _, err := strconv.Atoi(id) if err != nil { e := fmt.Sprintf("Invalid identifier") c.IndentedJSON(http.StatusNotFound, setResponse(e, false)) return } user := user{} dynStmt := `SELECT id_usuario,nombre,apellido1,apellido2,email,rol FROM usuarios WHERE id_usuario = $1` err = db.QueryRow(dynStmt, id).Scan(&user.Id, &user.Name, &user.Surname1, &user.Surname2, &user.Email, &user.AccountType) if err != nil { if err == sql.ErrNoRows { c.IndentedJSON(http.StatusNotFound, setResponse("User not found", false)) return } e := fmt.Sprintf("SOMETHING BAD HAPPENED QUERYING THE DATABASE: %v", err) log.Println(e) c.IndentedJSON(http.StatusInternalServerError, setResponse(e, false)) return } c.IndentedJSON(http.StatusOK, setResponse(user, true)) } func getUsers(c *gin.Context) { users := []user{} rows, err := db.Query("SELECT id_usuario,nombre,apellido1,apellido2,email,rol FROM usuarios") if err != nil { e := fmt.Sprintf("SOMETHING BAD HAPPENED QUERYING THE DATABASE: %v", err) log.Println(e) c.IndentedJSON(http.StatusInternalServerError, setResponse(e, false)) return } defer rows.Close() for rows.Next() { user := user{} err = rows.Scan(&user.Id, &user.Name, &user.Surname1, &user.Surname2, &user.Email, &user.AccountType) if err != nil { e := fmt.Sprintf("SOMETHING BAD HAPPENED SCANNING THE ROWS: %v", err) log.Println(e) c.IndentedJSON(http.StatusInternalServerError, setResponse(e, false)) return } users = append(users, user) } c.IndentedJSON(http.StatusOK, setResponse(users, true)) } func helloWorld(c *gin.Context) { ua := c.Request.UserAgent() message := fmt.Sprintf("Hello %v!\n", ua) c.String(http.StatusOK, message) }