diff options
author | Santo Cariotti <santo@dcariotti.me> | 2025-04-08 14:37:33 +0200 |
---|---|---|
committer | Santo Cariotti <santo@dcariotti.me> | 2025-04-08 14:39:13 +0200 |
commit | 1f0d9ec8452f15c27cd33c4e3874454c35993743 (patch) | |
tree | c453a31ae5eb823aaf48868eea9fc4daf65f108b /internal/api/handlers | |
parent | c5b10e28b358308d8349b940af09f64368172f2e (diff) |
Use internal/pkg structure
Diffstat (limited to 'internal/api/handlers')
-rw-r--r-- | internal/api/handlers/handlers.go | 197 | ||||
-rw-r--r-- | internal/api/handlers/utils.go | 34 |
2 files changed, 231 insertions, 0 deletions
diff --git a/internal/api/handlers/handlers.go b/internal/api/handlers/handlers.go new file mode 100644 index 0000000..b448502 --- /dev/null +++ b/internal/api/handlers/handlers.go @@ -0,0 +1,197 @@ +package handlers + +import ( + "encoding/json" + "log/slog" + "net/http" + "time" + + "github.com/boozec/rahanna/internal/api/auth" + "github.com/boozec/rahanna/internal/api/database" + "github.com/boozec/rahanna/internal/network" + "gorm.io/gorm" +) + +type NewGameRequest struct { + IP string `json:"ip"` +} + +func RegisterUser(w http.ResponseWriter, r *http.Request) { + slog.Info("POST /auth/register") + var user database.User + err := json.NewDecoder(r.Body).Decode(&user) + if err != nil { + JsonError(&w, err.Error()) + return + } + + if len(user.Password) < 4 { + JsonError(&w, "password too short") + return + } + + var storedUser database.User + db, _ := database.GetDb() + result := db.Where("username = ?", user.Username).First(&storedUser) + + if result.Error == nil { + JsonError(&w, "user with this username already exists") + return + } + + hashedPassword, err := HashPassword(user.Password) + if err != nil { + JsonError(&w, err.Error()) + return + } + user.Password = string(hashedPassword) + + result = db.Create(&user) + if result.Error != nil { + JsonError(&w, result.Error.Error()) + return + } + + token, err := auth.GenerateJWT(user.ID) + if err != nil { + JsonError(&w, err.Error()) + return + } + + json.NewEncoder(w).Encode(map[string]string{"token": token}) +} + +func LoginUser(w http.ResponseWriter, r *http.Request) { + slog.Info("POST /auth/login") + var inputUser database.User + err := json.NewDecoder(r.Body).Decode(&inputUser) + if err != nil { + JsonError(&w, err.Error()) + return + } + + var storedUser database.User + + db, _ := database.GetDb() + result := db.Where("username = ?", inputUser.Username).First(&storedUser) + if result.Error != nil { + JsonError(&w, "invalid credentials") + return + } + + if err := CheckPasswordHash(storedUser.Password, inputUser.Password); err != nil { + JsonError(&w, "invalid credentials") + return + } + + token, err := auth.GenerateJWT(storedUser.ID) + if err != nil { + JsonError(&w, err.Error()) + return + } + + json.NewEncoder(w).Encode(map[string]string{"token": token}) +} + +func NewPlay(w http.ResponseWriter, r *http.Request) { + slog.Info("POST /play") + claims, err := auth.ValidateJWT(r.Header.Get("Authorization")) + + if err != nil { + JsonError(&w, err.Error()) + return + } + + var payload struct { + IP string `json:"ip"` + } + + if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { + JsonError(&w, err.Error()) + return + } + + if err != nil { + JsonError(&w, err.Error()) + return + } + + db, _ := database.GetDb() + + name := network.NewSession() + play := database.Game{ + Player1ID: claims.UserID, + Player2ID: nil, + Name: name, + IP1: payload.IP, + IP2: "", + } + + result := db.Create(&play) + if result.Error != nil { + JsonError(&w, result.Error.Error()) + return + } + + json.NewEncoder(w).Encode(map[string]string{"name": name}) +} + +func EnterGame(w http.ResponseWriter, r *http.Request) { + slog.Info("POST /enter-game") + claims, err := auth.ValidateJWT(r.Header.Get("Authorization")) + + if err != nil { + JsonError(&w, err.Error()) + return + } + + var payload struct { + Name string `json:"name"` + IP string `json:"ip"` + } + + if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { + JsonError(&w, err.Error()) + return + } + + if err != nil { + JsonError(&w, err.Error()) + return + } + + db, _ := database.GetDb() + + var play database.Game + + result := db.Where("name = ? AND player2_id IS NULL", payload.Name).First(&play) + if result.Error != nil { + JsonError(&w, result.Error.Error()) + return + } + + play.Player2ID = &claims.UserID + play.IP2 = payload.IP + play.UpdatedAt = time.Now() + + if err := db.Save(&play).Error; err != nil { + JsonError(&w, err.Error()) + return + } + + result = db.Where("id = ?", play.ID). + Preload("Player1", func(db *gorm.DB) *gorm.DB { + return db.Omit("Password") + }). + Preload("Player2", func(db *gorm.DB) *gorm.DB { + return db.Omit("Password") + }). + First(&play) + + if result.Error != nil { + JsonError(&w, result.Error.Error()) + return + } + + json.NewEncoder(w).Encode(play) +} diff --git a/internal/api/handlers/utils.go b/internal/api/handlers/utils.go new file mode 100644 index 0000000..d6cc0d6 --- /dev/null +++ b/internal/api/handlers/utils.go @@ -0,0 +1,34 @@ +package handlers + +import ( + "encoding/json" + "net/http" + + "golang.org/x/crypto/bcrypt" +) + +func HashPassword(password string) (string, error) { + bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + return string(bytes), err +} + +func CheckPasswordHash(hash, password string) error { + return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) +} + +// Set a JSON response with status code 400 +func JsonError(w *http.ResponseWriter, error string) { + payloadMap := map[string]string{"error": error} + + (*w).Header().Set("Content-Type", "application/json") + (*w).WriteHeader(http.StatusBadRequest) + + payload, err := json.Marshal(payloadMap) + + if err != nil { + (*w).WriteHeader(http.StatusBadGateway) + (*w).Write([]byte(err.Error())) + } else { + (*w).Write(payload) + } +} |