Skip to content

Commit

Permalink
Merge pull request #10 from alik-r/misc_fixes
Browse files Browse the repository at this point in the history
Misc fixes
  • Loading branch information
RahmanTaghizade authored Nov 19, 2024
2 parents 8bd5f4e + 7cad8ac commit 3fd3354
Show file tree
Hide file tree
Showing 11 changed files with 273 additions and 104 deletions.
60 changes: 45 additions & 15 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,51 @@ on:
- main

jobs:
server_checks:
name: CI checks for server code
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
with:
ref: main
token: ${{ secrets.PERSONAL_GITHUB_TOKEN }}

- name: Set up Go environment
uses: actions/setup-go@v3
with:
go-version: '1.22'

- name: Cache Go modules
uses: actions/cache@v2
with:
path: |
~/.cache/go-build
key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-mod-
- name: Run Go Tests
run: |
cd backend
go test ./...
- name: Format Go code (go fmt)
run: |
cd backend
go fmt ./...
- name: Vet Go code (go vet)
run: |
cd backend
go vet ./...
deploy:
name: Deploy project
permissions:
deployments: write
runs-on: ubuntu-latest
needs: server_checks
steps:
- name: Checkout Repository
uses: actions/checkout@v2
Expand All @@ -37,23 +77,13 @@ jobs:
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: 22
script: |
# Set up GitHub authentication for pulling updates
git config --global url."https://${{ secrets.PERSONAL_GITHUB_TOKEN }}:@github.com".insteadOf "https://github.com"
# Navigate to project directory (assuming your docker-compose.yml is in the root)
cd /var/www/casino.alikamran.cc
# Pull the latest code from the main branch
git pull origin main
# Ensure Docker Compose is installed (if not already)
# if ! command -v docker-compose &> /dev/null; then
# echo "Docker Compose not found, installing..."
# sudo curl -L "https://github.com/docker/compose/releases/download/$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep -oP '"tag_name": "\K(.*)(?=")')" -o /usr/local/bin/docker-compose
# sudo chmod +x /usr/local/bin/docker-compose
# fi
# Build and start/restart containers with Docker Compose
sudo docker compose down # Stop existing containers, if any
sudo docker compose pull # Pull new images if they have changed
sudo docker compose build --no-cache # Rebuild images
sudo docker compose up -d # Start containers in detached mode
sudo docker compose down
sudo docker compose pull
sudo docker compose build --no-cache
sudo docker compose up -d
- name: Update deployment Status (success)
if: success()
Expand All @@ -71,4 +101,4 @@ jobs:
token: '${{ github.token }}'
environment-url: ${{ vars.MY_APP }}
state: 'failure'
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
1 change: 1 addition & 0 deletions backend/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func main() {
r.Get("/api/healthcheck", api.Healthcheck)

r.Post("/api/login", api.Login)
r.Post("/api/register", api.Register)

r.Group(func(r chi.Router) {
r.Use(middleware.JWTAuth)
Expand Down
6 changes: 4 additions & 2 deletions backend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@ replace github.com/alik-r/casino-roulette/backend/pkg/auth => ./pkg/auth

replace github.com/alik-r/casino-roulette/backend/pkg/middleware => ./pkg/middleware

require gorm.io/gorm v1.25.12
require (
golang.org/x/crypto v0.17.0
gorm.io/gorm v1.25.12
)

require (
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.5.5 // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/sync v0.8.0 // indirect
)

Expand Down
119 changes: 84 additions & 35 deletions backend/pkg/api/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ type LoginRequest struct {
Username string `json:"username"`
Email string `json:"email"`
Password string `json:"password"`
}

type RegisterRequest struct {
Username string `json:"username"`
Email string `json:"email"`
Password string `json:"password"`
Avatar string `json:"avatar,omitempty"`
}

Expand All @@ -36,64 +42,107 @@ type BetRequest struct {
func Login(w http.ResponseWriter, r *http.Request) {
var loginRequest LoginRequest
if err := json.NewDecoder(r.Body).Decode(&loginRequest); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
http.Error(w, "Invalid request payload", http.StatusBadRequest)
return
}

var user models.User
isRegister := false
query := db.DB.Where("email = ?", loginRequest.Email)
query := db.DB
if loginRequest.Username != "" {
query = query.Or("username = ?", loginRequest.Username)
query = query.Where("username = ?", loginRequest.Username)
} else if loginRequest.Email != "" {
query = query.Where("email = ?", loginRequest.Email)
} else {
http.Error(w, "Username or email is required", http.StatusBadRequest)
return
}

err := query.First(&user).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
if loginRequest.Username == "" || loginRequest.Email == "" {
http.Error(w, "Both username and email are required", http.StatusBadRequest)
return
}

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

user = models.User{
Username: loginRequest.Username,
Email: loginRequest.Email,
Avatar: loginRequest.Avatar,
Password: loginRequest.Password,
Balance: 1000,
}
if err := db.DB.Create(&user).Error; err != nil {
http.Error(w, "Failed to create user", http.StatusInternalServerError)
return
}
isRegister = true
http.Error(w, "User not found", http.StatusUnauthorized)
return
} else {
http.Error(w, "Error checking user", http.StatusInternalServerError)
http.Error(w, "Database error", http.StatusInternalServerError)
return
}
}

if !auth.CheckPasswordHash(loginRequest.Password, user.Password) {
http.Error(w, "Invalid password", http.StatusUnauthorized)
return
}

token, err := auth.GenerateJWT(user.Username)
if err != nil {
http.Error(w, "Token generation failed", http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"token": token,
})
}

func Register(w http.ResponseWriter, r *http.Request) {
var registerRequest RegisterRequest
if err := json.NewDecoder(r.Body).Decode(&registerRequest); err != nil {
http.Error(w, "Invalid request payload", http.StatusBadRequest)
return
}

var existingUser models.User
err := db.DB.Where("email = ?", registerRequest.Email).Or("username = ?", registerRequest.Username).First(&existingUser).Error
if err == nil {
http.Error(w, "Username or Email already exists", http.StatusConflict)
return
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
http.Error(w, "Database error", http.StatusInternalServerError)
return
}

if registerRequest.Avatar == "" {
registerRequest.Avatar = "images/avatars/avatar1.png"
} else {
if user.Password != loginRequest.Password {
http.Error(w, "Invalid password", http.StatusBadRequest)
return
parts := strings.Split(registerRequest.Avatar, "images/avatars/")
if len(parts) > 1 {
registerRequest.Avatar = "images/avatars/" + parts[1]
} else {
registerRequest.Avatar = "images/avatars/avatar1.png"
}
}

token, err := auth.GenerateJWT(user.Username)
hashedPassword, err := auth.HashPassword(registerRequest.Password)
log.Println("original password is ", registerRequest.Password, "hashed password is ", hashedPassword)
if err != nil {
http.Error(w, "Password hashing failed", http.StatusBadRequest)
return
}

newUser := models.User{
Username: registerRequest.Username,
Email: registerRequest.Email,
Password: hashedPassword,
Avatar: registerRequest.Avatar,
Balance: 1000,
}

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

token, err := auth.GenerateJWT(newUser.Username)
if err != nil {
http.Error(w, "Error generating token", http.StatusInternalServerError)
http.Error(w, "Token generation failed", http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"token": token,
"is_register": strconv.FormatBool(isRegister),
"token": token,
"message": "Registered successfully",
})
}

Expand Down
13 changes: 13 additions & 0 deletions backend/pkg/auth/hashing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package auth

import "golang.org/x/crypto/bcrypt"

func HashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
return string(bytes), err
}

func CheckPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
25 changes: 21 additions & 4 deletions frontend/leaderboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,23 +86,40 @@
return {
user: { username: '', balance: 0, avatar: '' },
leaderboard: [],
authToken: localStorage.getItem('authToken') || '',
loadLeaderboard() {
if (!this.authToken) {
alert('No auth token found. Please log in.');
window.location.href = '/register';
}
fetch('/api/user', {
method: 'GET',
headers: {
'Authorization': `Bearer ${localStorage.getItem('authToken') || ''}`
'Authorization': `Bearer ${this.authToken}`
}
})
.then(response => response.json())
.then(response => {
if (response.status === 401) {
alert('Invalid auth token. Please log in again.');
window.location.href = '/register';
}
return response.json();
})
.then(data => this.user = data);

fetch('/api/leaderboard', {
method: 'GET',
headers: {
'Authorization': `Bearer ${localStorage.getItem('authToken') || ''}`
'Authorization': `Bearer ${this.authToken}`
}
})
.then(response => response.json())
.then(response => {
if (response.status === 401) {
alert('Invalid auth token. Please log in again.');
window.location.href = '/register';
}
return response.json();
})
.then(data => this.leaderboard = data);
},
logout() {
Expand Down
28 changes: 23 additions & 5 deletions frontend/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@
</div>
<form class="deposit-form" @submit.prevent="deposit">
<label for="deposit-amount">Deposit Amount</label>
<input type="number" id="deposit-amount" x-model="depositAmount" placeholder="Enter amount" required>
<input type="number" id="deposit-amount" x-model="depositAmount" placeholder="Enter amount"
required>
<button type="submit" class="deposit-button">Deposit</button>
</form>
</div>
Expand All @@ -77,27 +78,44 @@
return {
user: { username: '', balance: 0, avatar: '' },
depositAmount: 0,
authToken: localStorage.getItem('authToken') || '',
loadProfile() {
if (!this.authToken) {
alert('No auth token found. Please log in.');
window.location.href = '/register';
}
fetch('/api/user', {
method: 'GET',
headers: {
'Authorization': `Bearer ${localStorage.getItem('authToken') || ''}`
'Authorization': `Bearer ${this.authToken}`
}
})
.then(response => response.json())
.then(response => {
if (response.status === 401) {
alert('Invalid auth token. Please log in again.');
window.location.href = '/register';
}
return response.json();
})
.then(data => this.user = data);
},
deposit() {
const depositAmount = parseInt(this.depositAmount);
fetch('/api/user/deposit', {
method: 'POST',
headers: {
'Authorization': `Bearer ${localStorage.getItem('authToken') || ''}`,
'Authorization': `Bearer ${this.authToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ amount: depositAmount })
})
.then(response => response.json())
.then(response => {
if (response.status === 401) {
alert('Invalid auth token. Please log in again.');
window.location.href = '/register';
}
return response.json();
})
.then(data => {
if (data.balance) {
this.user.balance = data.balance;
Expand Down
Loading

0 comments on commit 3fd3354

Please sign in to comment.