Skip to content

Commit

Permalink
Merge pull request #8 from alik-r/roulette
Browse files Browse the repository at this point in the history
Implement Roulette page
  • Loading branch information
alik-r authored Nov 16, 2024
2 parents f5e1352 + e61311f commit 57843bb
Show file tree
Hide file tree
Showing 12 changed files with 873 additions and 104 deletions.
5 changes: 3 additions & 2 deletions backend/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ func main() {
r.Get("/api/healthcheck", api.Healthcheck)

r.Post("/api/login", api.Login)
r.Get("/api/leaderboard", api.GetLeaderboard)

r.Group(func(r chi.Router) {
r.Use(middleware.JWTAuth)
r.Get("/api/user", api.GetUser)
r.Post("/api/user/deposit", api.Deposit)
r.Post("/api/roulette/bet", api.PlaceBet)
r.Get("/api/leaderboard", api.GetLeaderboard)
r.Post("/api/roulette", api.PlaceBet)
})

log.Println("Server running on port 8080")
Expand Down
145 changes: 119 additions & 26 deletions backend/pkg/api/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package api
import (
"encoding/json"
"errors"
"log"
"net/http"
"strconv"
"strings"

"github.com/alik-r/casino-roulette/backend/pkg/auth"
"github.com/alik-r/casino-roulette/backend/pkg/db"
Expand All @@ -26,8 +28,9 @@ type DepositRequest struct {
}

type BetRequest struct {
BetAmount int `json:"bet_amount"`
BetColor string `json:"bet_color"`
BetAmount int `json:"bet_amount"`
BetType string `json:"bet_type"`
BetValue interface{} `json:"bet_value"`
}

func Login(w http.ResponseWriter, r *http.Request) {
Expand All @@ -54,6 +57,8 @@ func Login(w http.ResponseWriter, r *http.Request) {

if loginRequest.Avatar == "" {
loginRequest.Avatar = "images/avatars/avatar1.png"
} else {
loginRequest.Avatar = "images/avatars/" + strings.Split(loginRequest.Avatar, "images/avatars/")[1]
}

user = models.User{
Expand Down Expand Up @@ -95,7 +100,7 @@ func Login(w http.ResponseWriter, r *http.Request) {
func Deposit(w http.ResponseWriter, r *http.Request) {
username, ok := r.Context().Value(middleware.UsernameKey).(string)
if !ok || username == "" {
http.Error(w, "missing username", http.StatusBadRequest)
http.Error(w, "Unauthenticated user", http.StatusUnauthorized)
return
}

Expand Down Expand Up @@ -137,89 +142,177 @@ func Deposit(w http.ResponseWriter, r *http.Request) {
func PlaceBet(w http.ResponseWriter, r *http.Request) {
username, ok := r.Context().Value(middleware.UsernameKey).(string)
if !ok || username == "" {
http.Error(w, "missing username", http.StatusBadRequest)
http.Error(w, "Unauthenticated user", http.StatusUnauthorized)
return
}

var betRequest BetRequest
err := json.NewDecoder(r.Body).Decode(&betRequest)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
http.Error(w, "Invalid request payload", http.StatusBadRequest)
return
}

if betRequest.BetAmount <= 0 {
http.Error(w, "Bet amount must be greater than zero", http.StatusBadRequest)
return
}

if betRequest.BetAmount <= 0 || !roulette.IsValidColor(betRequest.BetColor) {
http.Error(w, "invalid bet amount or color", http.StatusBadRequest)
if !roulette.IsValidBet(roulette.BetType(betRequest.BetType), betRequest.BetValue) {
log.Println("Invalid bet type or value:", betRequest.BetType, roulette.BetType(betRequest.BetType), betRequest.BetValue)
http.Error(w, "Invalid bet type or value", http.StatusBadRequest)
return
}

var user models.User
err = db.DB.Where("username = ?", username).First(&user).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
http.Error(w, "user not found", http.StatusNotFound)
http.Error(w, "User not found", http.StatusNotFound)
return
}
http.Error(w, err.Error(), http.StatusInternalServerError)
http.Error(w, "Database error", http.StatusInternalServerError)
log.Println("Database error in PlaceBet:", err)
return
}

if user.Balance < betRequest.BetAmount {
http.Error(w, "insufficient balance", http.StatusBadRequest)
http.Error(w, "Insufficient balance", http.StatusBadRequest)
return
}

result := roulette.Spin()
payout := roulette.Payout(betRequest.BetColor, string(result.Color))

payoutMultiplier := roulette.Payout(roulette.BetType(betRequest.BetType), betRequest.BetValue, result)
var payout int
var betResult string
if payout > 0 {
user.Balance += betRequest.BetAmount * (payout - 1)
if payoutMultiplier > 0 {
payout = betRequest.BetAmount * payoutMultiplier
user.Balance += payout
betResult = "win"
} else {
payout = 0
user.Balance -= betRequest.BetAmount
betResult = "lose"
}

if err := db.DB.Save(&user).Error; err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
http.Error(w, "Failed to update user balance", http.StatusInternalServerError)
return
}

var betValueStr string
switch betRequest.BetType {
case "color", "evenodd", "highlow":
value, ok := betRequest.BetValue.(string)
if !ok {
log.Printf("Invalid bet value, expected string, got %T", betRequest.BetValue)
http.Error(w, "Invalid bet value", http.StatusBadRequest)
return
}
betValueStr = value
case "number":
value, ok := betRequest.BetValue.(float64)
if !ok {
log.Printf("Invalid bet value, expected float64, got %T", betRequest.BetValue)
http.Error(w, "Invalid bet value", http.StatusBadRequest)
return
}
betValueStr = strconv.Itoa(int(value))
default:
log.Println("Invalid bet type:", betRequest.BetType)
http.Error(w, "Invalid bet type", http.StatusBadRequest)
return
}

bet := models.Bet{
UserID: user.ID,
BetAmount: betRequest.BetAmount,
BetColor: betRequest.BetColor,
BetType: betRequest.BetType,
BetValue: betValueStr,
Payout: payout,
Result: betResult,
}

if err := db.DB.Create(&bet).Error; err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
http.Error(w, "Failed to record bet", http.StatusInternalServerError)
return
}

response := map[string]interface{}{
"username": user.Username,
"balance": user.Balance,
"bet_amount": bet.BetAmount,
"bet_color": bet.BetColor,
"result": bet.Result,
"result_color": result.Color,
"balance": user.Balance,
"bet_amount": bet.BetAmount,
"bet_type": bet.BetType,
"bet_value": bet.BetValue,
"payout": bet.Payout,
"result": bet.Result,
"result_color": result.Color,
"result_number": result.Number,
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(response)
}

func GetLeaderboard(w http.ResponseWriter, r *http.Request) {
username, ok := r.Context().Value(middleware.UsernameKey).(string)
if !ok || username == "" {
http.Error(w, "Unauthenticated user", http.StatusUnauthorized)
return
}

var users []models.User
err := db.DB.Order("balance desc").Limit(10).Find(&users).Error
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
if err := db.DB.Order("balance DESC").Limit(10).Find(&users).Error; err != nil {
http.Error(w, "Failed to retrieve leaderboard", http.StatusInternalServerError)
return
}

type LeaderboardRow struct {
Username string `json:"username"`
Balance int `json:"balance"`
Avatar string `json:"avatar"`
}

var leaderboard []LeaderboardRow
for _, user := range users {
leaderboard = append(leaderboard, LeaderboardRow{
Username: user.Username,
Balance: user.Balance,
Avatar: user.Avatar,
})
}

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(leaderboard)
}

func GetUser(w http.ResponseWriter, r *http.Request) {
username, ok := r.Context().Value(middleware.UsernameKey).(string)
if !ok || username == "" {
http.Error(w, "Unauthenticated user", http.StatusUnauthorized)
return
}

var user models.User
if err := db.DB.Where("username = ?", username).First(&user).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
http.Error(w, "User not found", http.StatusNotFound)
} else {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}

response := map[string]interface{}{
"username": user.Username,
"email": user.Email,
"avatar": user.Avatar,
"balance": user.Balance,
}

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(users)
json.NewEncoder(w).Encode(response)
}

func Healthcheck(w http.ResponseWriter, r *http.Request) {
Expand Down
4 changes: 3 additions & 1 deletion backend/pkg/models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ type Bet struct {
UserID uint `json:"user_id"`
User User `gorm:"foreignKey:UserID" json:"-"`
BetAmount int `json:"bet_amount"`
BetColor string `json:"bet_color"`
BetType string `json:"bet_type"`
BetValue string `json:"bet_value"`
Payout int `json:"payout"`
Result string `json:"result"`
CreatedAt time.Time `json:"-" gorm:"autoCreateTime"`
}
Loading

0 comments on commit 57843bb

Please sign in to comment.