M sdbapi/db/game.go => sdbapi/db/game.go +2 -3
@@ 1,6 1,6 @@
/*
* This file is part of sdbapi
- * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
+ * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
@@ 16,11 16,10 @@ import (
// get games for a specific user with email
func GetGamesForUser(id string) ([]models.Game, *apierror.APIError) {
var games []models.Game
- game_records := DbConn.Where("p1 = ? OR p2 = ?", id, id).Find(&games)
+ game_records := DbConn.Preload("Host").Preload("Guest").Where("host_id = ? OR guest_id = ?", id, id).Find(&games)
if game_records.Error != nil {
return []models.Game{}, &apierror.NoGamesForUser
}
return games, nil
}
-
M sdbapi/handlers/allforming.go => sdbapi/handlers/allforming.go +2 -3
@@ 1,6 1,6 @@
/*
* This file is part of sdbapi
- * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
+ * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
@@ 19,7 19,7 @@ import (
func FormingGames(c *gin.Context) {
var games []models.Game
- records := db.DbConn.Where("state = ?", models.GAMESTATE_FORMING).Find(&games)
+ records := db.DbConn.Preload("Host").Preload("Guest").Where("state = ?", models.GAMESTATE_FORMING).Find(&games)
if records.Error != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": apierror.GetAllFormingFailed})
c.Abort()
@@ 28,4 28,3 @@ func FormingGames(c *gin.Context) {
c.JSON(http.StatusOK, games)
}
-
M sdbapi/handlers/gamecreate.go => sdbapi/handlers/gamecreate.go +3 -3
@@ 1,6 1,6 @@
/*
* This file is part of sdbapi
- * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
+ * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
@@ 18,11 18,11 @@ import (
)
func CreateGame(c *gin.Context) {
- p1, _ := db.GetUserByEmail(c.GetString("email"))
+ host, _ := db.GetUserByEmail(c.GetString("email"))
var game models.Game
- game.P1 = p1.ID
+ game.HostID = host.ID
game.State = models.GAMESTATE_FORMING
entry := db.DbConn.Create(&game)
M sdbapi/handlers/gameinfo.go => sdbapi/handlers/gameinfo.go +3 -3
@@ 1,6 1,6 @@
/*
* This file is part of sdbapi
- * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
+ * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
@@ 20,9 20,9 @@ import (
func GameInfo(c *gin.Context) {
id := c.Param("id")
- // Check if the game exists
+ // check if the game exists
var game models.Game
- record := db.DbConn.Where("id = ?", id).First(&game)
+ record := db.DbConn.Preload("Host").Preload("Guest").Where("id = ?", id).First(&game)
if record.Error != nil {
c.JSON(http.StatusNotFound, gin.H{"error": apierror.GameNotFound})
c.Abort()
M sdbapi/handlers/joingame.go => sdbapi/handlers/joingame.go +10 -19
@@ 1,6 1,6 @@
/*
* This file is part of sdbapi
- * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
+ * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
@@ 29,8 29,8 @@ func JoinGame(c *gin.Context) {
return
}
- // check if p2 is already filled
- if game.P2 != "" {
+ // check if guest is already filled
+ if game.GuestID != "" {
c.JSON(http.StatusBadRequest, gin.H{"error": apierror.GameFull})
c.Abort()
return
@@ 43,31 43,23 @@ func JoinGame(c *gin.Context) {
return
}
- // get p1 (game creator)
- p1, p1err := db.GetUserByID(game.P1)
- if p1err != nil {
+ // get guest (us)
+ newGuest, newGuestErr := db.GetUserByEmail(c.GetString("email"))
+ if newGuestErr != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": apierror.Placeholder})
c.Abort()
return
}
- // get p2 (us)
- p2, p2err := db.GetUserByEmail(c.GetString("email"))
- if p2err != nil {
- c.JSON(http.StatusBadRequest, gin.H{"error": apierror.Placeholder})
- c.Abort()
- return
- }
-
- // are we p1?
- if p1.ID == p2.ID {
+ // are we the host?
+ if game.HostID == newGuest.ID {
c.JSON(http.StatusBadRequest, gin.H{"error": apierror.CannotJoinOwnGame})
c.Abort()
return
}
- // if we are not p1, set us as p2
- updatedGame := db.DbConn.Model(&game).Update("p2", p2.ID)
+ // if we are not the host, set us as the guest
+ updatedGame := db.DbConn.Model(&game).Update("guest_id", newGuest.ID)
if updatedGame.Error != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": apierror.Placeholder})
c.Abort()
@@ 77,4 69,3 @@ func JoinGame(c *gin.Context) {
// return a 20x code
c.JSON(http.StatusNoContent, nil)
}
-
M sdbapi/handlers/patchgamestate.go => sdbapi/handlers/patchgamestate.go +5 -5
@@ 20,7 20,7 @@ import (
func PatchGameState(c *gin.Context) {
id := c.Param("id")
- // Check if the game exists
+ // check if the game exists
var game models.Game
record := db.DbConn.Where("id = ?", id).First(&game)
if record.Error != nil {
@@ 29,9 29,9 @@ func PatchGameState(c *gin.Context) {
return
}
- // Get the user who requested the patching, and verify that they are the game creator (p1)
- p1, _ := db.GetUserByEmail(c.GetString("email"))
- if game.P1 != p1.ID {
+ // get the user who requested the patching, and verify that they are the game host
+ presumedHost, _ := db.GetUserByEmail(c.GetString("email"))
+ if game.HostID != presumedHost.ID {
c.JSON(http.StatusUnauthorized, gin.H{"error": apierror.NotAuthorized})
c.Abort()
return
@@ 51,6 51,6 @@ func PatchGameState(c *gin.Context) {
return
}
- // Don't have anything to return
+ // don't have anything to return
c.JSON(http.StatusNoContent, nil)
}
M sdbapi/handlers/usercreate.go => sdbapi/handlers/usercreate.go +11 -6
@@ 19,22 19,22 @@ import (
)
func CreateUser(c *gin.Context) {
- var user models.User
- if err := c.ShouldBindJSON(&user); err != nil {
+ var input models.UserCreateInput
+ if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": apierror.InvalidInput})
c.Abort()
return
}
// check username
- if len(user.Username) < 3 {
+ if len(input.Username) < 3 {
c.JSON(http.StatusBadRequest, gin.H{"error": apierror.UsernameTooShort})
c.Abort()
return
}
// check email
- _, mailErr := mail.ParseAddress(user.Email)
+ _, mailErr := mail.ParseAddress(input.Email)
if mailErr != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": apierror.EmailInvalid})
c.Abort()
@@ 42,12 42,17 @@ func CreateUser(c *gin.Context) {
}
// check password
- if len(user.Password) < 8 {
+ if len(input.Password) < 8 {
c.JSON(http.StatusBadRequest, gin.H{"error": apierror.PasswordTooShort})
c.Abort()
return
}
+ var user models.User
+ user.Username = input.Username
+ user.Email = input.Email
+ user.Password = input.Password
+
if err := user.HashPwd(user.Password); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": apierror.PasswordHashFailed})
c.Abort()
@@ 61,5 66,5 @@ func CreateUser(c *gin.Context) {
return
}
- c.JSON(http.StatusCreated, gin.H{"id": user.ID, "username": user.Username, "email": user.Email})
+ c.JSON(http.StatusCreated, user.ToPub())
}
M sdbapi/handlers/userinfo.go => sdbapi/handlers/userinfo.go +2 -2
@@ 1,6 1,6 @@
/*
* This file is part of sdbapi
- * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
+ * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
@@ 28,5 28,5 @@ func UserInfo(c *gin.Context) {
return
}
- c.JSON(http.StatusOK, gin.H{"id": user.ID, "username": user.Username})
+ c.JSON(http.StatusOK, user.ToPub())
}
M sdbapi/models/game.go => sdbapi/models/game.go +6 -4
@@ 1,6 1,6 @@
/*
* This file is part of sdbapi
- * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
+ * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
@@ 22,12 22,14 @@ const (
)
type Game struct {
- ID string `json:"id" gorm:"primarykey;type:uuid;default:gen_random_uuid()"`
+ ID string `json:"id" gorm:"primarykey;type:uuid;default:gen_random_uuid()"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"`
- P1 string `json:"p1" gorm:"foreignkey:ID"`
- P2 string `json:"p2" gorm:"foreignkey:ID"`
+ HostID string `json:"host_id"`
+ Host User `json:"host" gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
+ GuestID string `json:"guest_id" gorm:"default:NULL;"`
+ Guest User `json:"guest" gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
State uint8 `json:"state" gorm:"type:smallint"`
EndedAt time.Time `json:"ended_at" gorm:"type:timestamptz"`
}
M sdbapi/models/user.go => sdbapi/models/user.go +28 -5
@@ 1,6 1,6 @@
/*
* This file is part of sdbapi
- * Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
+ * Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
@@ 16,13 16,19 @@ import (
)
type User struct {
- ID string `json:"id" gorm:"primarykey;type:uuid;default:gen_random_uuid()"`
+ ID string `json:"id" gorm:"primarykey;type:uuid;default:gen_random_uuid()"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
- DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"`
+ DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
Username string `json:"username" gorm:"unique"`
- Email string `json:"email" gorm:"unique"`
- Password string `json:"password"`
+ Email string `json:"-" gorm:"unique"`
+ Password string `json:"-"`
+}
+
+type UserCreateInput struct {
+ Username string `json:"username"`
+ Email string `json:"email"`
+ Password string `json:"password"`
}
// hash a users password
@@ 42,3 48,20 @@ func (user *User) VerifyPwd(password string) error {
}
return nil
}
+
+type UserPub struct {
+ ID string `json:"id"`
+ CreatedAt time.Time `json:"created_at"`
+ UpdatedAt time.Time `json:"updated_at"`
+ Username string `json:"username"`
+}
+
+// Convert a user to UserPub, which can be returned safely
+func (user *User) ToPub() UserPub {
+ return UserPub{
+ ID: user.ID,
+ CreatedAt: user.CreatedAt,
+ UpdatedAt: user.UpdatedAt,
+ Username: user.Username,
+ }
+}