ADD: added database connection for players data handling and started login funtion with database

This commit is contained in:
hwinkel
2025-05-30 15:02:23 +02:00
parent 4158b87576
commit 1a2eec44a9
19 changed files with 924 additions and 58 deletions

View File

@@ -1,8 +1,11 @@
package auth
import (
"database/sql"
"net/http"
"time"
"volleyball/internal/common"
"volleyball/internal/player"
"github.com/gin-gonic/gin"
)
@@ -16,23 +19,53 @@ type LoginResponse struct {
Token string `json:"token"`
}
func LoginHandler(c *gin.Context) {
func LoginHandler(c *gin.Context, db *sql.DB) {
var req LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Bad request"})
return
}
// Systemnutzer
if req.Email == "test@localhost.de" {
token, err := CreateJWT("system-user-id", req.Email, "admin", time.Hour*24*7)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Token error"})
return
}
c.JSON(http.StatusOK, LoginResponse{Token: token})
// Validate input
if req.Email == "" || req.Password == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "Email and password are required"})
return
}
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})
// Systemnutzer
var token string
var err error
if req.Email == "test@localhost.de" {
token, err = CreateJWT("system-user-id", req.Email, "admin", 60*time.Minute)
} else {
hash, err := common.HashPassword(req.Password)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Password hashing error"})
return
}
loggedInPlayer, err := player.LoginPlayer(db, req.Email, string(hash))
if err != nil {
if err == sql.ErrNoRows {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "Database error"})
return
}
// Create JWT token
token, err = CreateJWT(loggedInPlayer.ID, req.Email, "player", 60*time.Minute)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Token creation error"})
return
}
}
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Token error"})
return
}
c.JSON(http.StatusOK, LoginResponse{Token: token})
return
}

View File

@@ -0,0 +1,12 @@
package common
import "golang.org/x/crypto/bcrypt"
func HashPassword(password string) (string, error) {
// Use bcrypt to hash the password
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return "", err
}
return string(hashedPassword), nil
}

View File

@@ -17,6 +17,29 @@ type database struct {
db *sql.DB // Pointer to sql.DB
}
func (d *database) SetupTables() error {
InitTables(d.db)
log.Println("Database setup completed successfully.")
return nil
}
func (d *database) GetDB() *sql.DB {
if d.db == nil {
log.Println("Database connection is not established. Call Connect() first.")
return nil
}
return d.db
}
// New creates a new database instance with the provided configuration.
// It initializes the database connection parameters but does not connect to the database.
// This function should be called before calling Connect() to establish the connection.
// It returns a pointer to the database instance.
// Example usage:
// db := database.New("localhost", 5432, "user", "password", "dbname")
// It is intended to be used in the main application or setup phase.
// This function is not intended to be called during normal application operation.
// It is not intended to be called during normal application operation.
func New(host string, port int, user, password, dbname string) *database {
return &database{
host: host,
@@ -28,7 +51,7 @@ func New(host string, port int, user, password, dbname string) *database {
}
}
func (d *database) Connect() error {
func (d *database) Connect() (db *sql.DB) {
fmt.Println("Connecting to the database...")
psqlInfo := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
d.host, d.port, d.user, d.password, d.dbname)
@@ -49,5 +72,5 @@ func (d *database) Connect() error {
log.Println("Connected to the database successfully")
d.db = db
return nil
return db
}

View File

@@ -5,28 +5,66 @@ import (
"fmt"
"log"
"volleyball/internal/player"
_ "github.com/lib/pq" // Import the PostgreSQL driver
)
const playerTable = `
CREATE TABLE IF NOT EXISTS players (
const teamTable = `
CREATE TABLE IF NOT EXISTS teams (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
age INT NOT NULL,
birthday DATE NOT NULL
player1_id uuid NOT NULL,
player2_id uuid NOT NULL,
formation_date DATE NOT NULL DEFAULT CURRENT_DATE,
FOREIGN KEY (player1_id) REFERENCES players(id),
FOREIGN KEY (player2_id) REFERENCES players(id)
);
`
func InitTables(db *sql.DB) {
var tableNames = []string{
"players",
"teams",
}
func CheckIfTablesExist(db *sql.DB) (bool, error) {
for _, tableName := range tableNames {
exists, err := tableExists(db, tableName)
if err != nil {
return false, fmt.Errorf("error checking if table %s exists: %w", tableName, err)
}
if !exists {
return false, nil // At least one table does not exist
}
}
return true, nil // All tables exist
}
func tableExists(db *sql.DB, tableName string) (bool, error) {
query := `
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = 'public' AND table_name = $1
);
`
var exists bool
err := db.QueryRow(query, tableName).Scan(&exists)
return exists, err
}
func InitTables(d *sql.DB) error {
tables := []string{
playerTable,
player.PlayerTable,
player.RoleTable,
teamTable,
}
for _, table := range tables {
if _, err := db.Exec(table); err != nil {
if _, err := d.Exec(table); err != nil {
log.Fatalf("Error creating table: %v", err)
return fmt.Errorf("error creating table: %w", err)
}
}
fmt.Println("Tables initialized successfully.")
log.Println("Tables initialized successfully.")
return nil
}

View File

@@ -0,0 +1,145 @@
package player
import (
"database/sql"
"log"
"net/http"
"volleyball/internal/common"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
func GetPlayers(c *gin.Context, db *sql.DB) {
log.Println(c.GetString("userId"), c.GetString("email"), c.GetString("role"))
// Simulate fetching players from a database
players, err := GetAllPlayers(db)
if err != nil {
log.Printf("Error retrieving players: %v", err)
common.RespondError(c, http.StatusInternalServerError, "Failed to retrieve players")
return
}
if len(players) > 0 {
log.Printf("User %s (%s) requested players", c.GetString("userId"), c.GetString("email"))
c.JSON(http.StatusOK, players)
return
}
log.Printf("User %s (%s) requested players, but none found", c.GetString("userId"), c.GetString("email"))
common.RespondError(c, http.StatusNotFound, "No Players found")
}
func CreatePlayer(c *gin.Context, db *sql.DB) {
var newPlayer Player
var err error
if err := c.ShouldBindJSON(&newPlayer); err != nil {
log.Printf("Error binding player data: %v", err)
common.RespondError(c, http.StatusBadRequest, "Invalid player data")
return
}
newPlayer.Password, err = common.HashPassword(newPlayer.Password)
if err != nil {
log.Printf("Error hashing password: %v", err)
common.RespondError(c, http.StatusInternalServerError, "Failed to hash password")
return
}
newPlayer.ID = uuid.New().String()
err = savePlayer(db, newPlayer)
if err != nil {
log.Printf("Error saving player: %v", err)
common.RespondError(c, http.StatusInternalServerError, "Failed to create player")
return
}
AddRoleToPlayer(db, newPlayer.ID, "player")
if err != nil {
log.Printf("Error adding role to player: %v", err)
common.RespondError(c, http.StatusInternalServerError, "Failed to assign role to player")
return
}
log.Printf("User %s (%s) created player: %s", c.GetString("userId"), c.GetString("email"), newPlayer.Name)
common.RespondCreated(c, newPlayer)
}
func GetPlayer(c *gin.Context, db *sql.DB) {
playerID := c.Param("id")
if playerID == "" {
common.RespondError(c, http.StatusBadRequest, "Player ID is required")
return
}
player, err := GetPlayerByID(db, playerID)
if err != nil {
log.Printf("Error retrieving player with ID %s: %v", playerID, err)
common.RespondError(c, http.StatusInternalServerError, "Failed to retrieve player")
return
}
if player.ID == "" {
log.Printf("Player with ID %s not found", playerID)
common.RespondError(c, http.StatusNotFound, "Player not found")
return
}
log.Printf("User %s (%s) requested player: %s", c.GetString("userId"), c.GetString("email"), player.Name)
c.JSON(http.StatusOK, player)
}
func UpdatePlayer(c *gin.Context, db *sql.DB) {
playerID := c.Param("id")
if playerID == "" {
common.RespondError(c, http.StatusBadRequest, "Player ID is required")
return
}
if playerID != c.GetString("userId") || c.GetString("role") != "admin" {
common.RespondError(c, http.StatusForbidden, "You do not have permission to update this player")
return
}
var updatedPlayer Player
if err := c.ShouldBindJSON(&updatedPlayer); err != nil {
log.Printf("Error binding player data: %v", err)
common.RespondError(c, http.StatusBadRequest, "Invalid player data")
return
}
updatedPlayer.ID = playerID
err := updatePlayer(db, updatedPlayer)
if err != nil {
log.Printf("Error updating player with ID %s: %v", playerID, err)
common.RespondError(c, http.StatusInternalServerError, "Failed to update player")
return
}
log.Printf("User %s (%s) updated player: %s", c.GetString("userId"), c.GetString("email"), updatedPlayer.Name)
c.JSON(http.StatusOK, updatedPlayer)
}
func DeletePlayer(c *gin.Context, db *sql.DB) {
playerID := c.Param("id")
if playerID == "" {
common.RespondError(c, http.StatusBadRequest, "Player ID is required")
return
}
if c.GetString("role") != "admin" {
common.RespondError(c, http.StatusForbidden, "You do not have permission to delete this player")
return
}
err := deletePlayer(db, playerID)
if err != nil {
log.Printf("Error deleting player with ID %s: %v", playerID, err)
common.RespondError(c, http.StatusInternalServerError, "Failed to delete player")
return
}
log.Printf("User %s (%s) deleted player with ID: %s", c.GetString("userId"), c.GetString("email"), playerID)
common.RespondMessage(c, "Player deleted successfully")
}

View File

@@ -0,0 +1,286 @@
package player
import (
"database/sql"
"log"
"github.com/google/uuid"
)
const PlayerTable = `
CREATE TABLE IF NOT EXISTS players (
id uuid PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL
);
`
const RoleTable = `
CREATE TABLE IF NOT EXISTS roles (
id SERIAL PRIMARY KEY,
player_id uuid NOT NULL,
role VARCHAR(50) NOT NULL CHECK (role IN ('player', 'admin')),
FOREIGN KEY (player_id) REFERENCES players(id) ON DELETE CASCADE
);
`
type Team struct {
ID string `json:"id"`
Name string `json:"name"`
Players []Player `json:"players"` // Players in the team
// Matches []Match `json:"matches"` // Matches the team is involved in
MatchesWon int `json:"matchesWon"` // Number of matches won by the team
MatchesLost int `json:"matchesLost"` // Number of matches lost by the team
MatchesDrawn int `json:"matchesDrawn"` // Number of matches drawn by the team
MatchesPlayed int `json:"matchesPlayed"` // Total matches played by the team
MatchesPlayedCount int `json:"matchesPlayedCount"` // Total matches played by the team
}
type Player struct {
ID string `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Password string `json:"password,omitempty"` // Password is optional for responses
Role string `json:"role"` // e.g., "player","organizer"
Teams []Team `json:"teams"` // Teams the player is part of
// Tournaments []Tournament `json:"tournaments"` // Tournaments the player is registered in
OwnedTournaments []string `json:"ownedTournaments"` // Tournaments the player is organizing
ParticipatedTournaments []string `json:"participatedTournaments"` // Tournaments the player is participating in
}
type PlayerListResponse struct {
Players []Player `json:"players"`
}
type PlayerResponse struct {
Player Player `json:"player"`
}
type CreatePlayerRequest struct {
Name string `json:"name" binding:"required"`
Email string `json:"email" binding:"required,email"`
Role string `json:"role" binding:"required,oneof=player organizer"`
}
type UpdatePlayerRequest struct {
Name string `json:"name" binding:"required"`
Email string `json:"email" binding:"required,email"`
Role string `json:"role" binding:"required,oneof=player organizer"`
Teams []Team `json:"teams"` // Teams the player is part of
}
type DeletePlayerRequest struct {
ID string `json:"id" binding:"required"`
}
type PlayerService interface {
CreatePlayer(req CreatePlayerRequest) (Player, error)
GetPlayer(id string) (Player, error)
UpdatePlayer(id string, req UpdatePlayerRequest) (Player, error)
DeletePlayer(id string) error
ListPlayers() ([]Player, error)
GetPlayerTeams(id string) ([]Team, error)
GetPlayerByEmail(email string) (Player, error)
GetPlayerByName(name string) (Player, error)
GetPlayerByID(id string) (Player, error)
GetPlayerByRole(role string) ([]Player, error)
GetPlayerByTeam(teamID string) ([]Player, error)
GetPlayerByTournament(tournamentID string) ([]Player, error)
GetPlayerByOrganizer(organizerID string) ([]Player, error)
}
func LoginPlayer(db *sql.DB, email, password string) (Player, error) {
var player Player
var playerPassword string
err := db.QueryRow("SELECT id, name, email, password FROM players WHERE email = $1", email).Scan(&player.ID, &player.Name, &player.Email, playerPassword)
if err != nil {
log.Printf("Error logging in player with email %s: %v", email, err)
return Player{}, err
}
if player.ID == "" {
log.Printf("Player with email %s not found", email)
return Player{}, sql.ErrNoRows // or a custom error
}
// Check if the password matches
if password == playerPassword {
log.Printf("Player %s logged in successfully", player.Name)
}
log.Printf("Player %s logged in successfully", player.Name)
return player, nil
}
func GetPlayerRole(db *sql.DB, playerID string) (string, error) {
var role string
err := db.QueryRow("SELECT role FROM roles WHERE player_id = $1", playerID).Scan(&role)
if err != nil {
log.Printf("Error retrieving role for player ID %s: %v", playerID, err)
return "", err
}
if role == "" {
log.Printf("No role found for player ID %s", playerID)
return "", sql.ErrNoRows // or a custom error
}
log.Printf("Retrieved role for player ID %s: %s", playerID, role)
return role, nil
}
func savePlayer(db *sql.DB, player Player) error {
// Ensure the player ID is set
if player.ID == "" {
player.ID = uuid.New().String() // Generate a new UUID if not set
log.Printf("Generated new player ID: %s", player.ID)
}
// Insert the player into the database
if player.Role == "" {
player.Role = "player" // Default role if not specified
log.Printf("Setting default role for player %s to 'player'", player.Name)
}
log.Printf("Saving player: ID=%v, Name=%v, Email=%v", player.ID, player.Name, player.Email)
stmt := "INSERT INTO public.players (id, name, email) VALUES ($1, $2, $3)"
log.Printf("Generated SQL statement: %s", stmt)
_, err := db.Exec(stmt, player.ID, player.Name, player.Email)
if err != nil {
log.Printf("Error saving player to database: %v", err)
}
// lastID, err := res.LastInsertId()
// if err != nil {
// log.Printf("Error getting last insert ID: %v", err)
// } else {
// log.Printf("Last insert ID: %d", lastID)
// }
// if err := db.QueryRow("SELECT id, name, email FROM players WHERE id = ?", player.ID).Scan(&player.ID, &player.Name, &player.Email, &player.Role); err != nil {
// log.Printf("Error retrieving player from database: %v", err)
// return err
// }
log.Printf("Player %s saved to database", player.Name)
return nil
}
func AddRoleToPlayer(db *sql.DB, playerID, role string) error {
if playerID == "" {
log.Printf("Player ID is required to add a role, but got empty ID")
return sql.ErrNoRows // or a custom error
}
log.Printf("Adding role '%s' to player with ID: %s", role, playerID)
stmt := "INSERT INTO public.roles (player_id, role) VALUES ($1, $2)"
_, err := db.Exec(stmt, playerID, role)
if err != nil {
log.Printf("Error adding role to player with ID %s: %v", playerID, err)
return err
}
log.Printf("Role '%s' added to player with ID %s successfully", role, playerID)
return nil
}
func GetAllPlayers(db *sql.DB) ([]Player, error) {
rows, err := db.Query("SELECT id, name, email FROM public.players")
if err != nil {
log.Printf("Error retrieving players: %v", err)
return nil, err
}
defer rows.Close()
var players []Player
for rows.Next() {
var player Player
if err := rows.Scan(&player.ID, &player.Name, &player.Email); err != nil {
log.Printf("Error scanning player row: %v", err)
return nil, err
}
players = append(players, player)
}
if err := rows.Err(); err != nil {
log.Printf("Error iterating over player rows: %v", err)
return nil, err
}
return players, nil
}
func GetPlayerByID(db *sql.DB, id string) (Player, error) {
var player Player
err := db.QueryRow("SELECT id, name, email FROM players WHERE id = $1", id).Scan(&player.ID, &player.Name, &player.Email, &player.Role)
if err != nil {
log.Printf("Error retrieving player by ID %s: %v", id, err)
return Player{}, err
}
return player, nil
}
func GetPlayerByEmail(db *sql.DB, email string) (Player, error) {
var player Player
err := db.QueryRow("SELECT id, name, email FROM players WHERE email = $1", email).Scan(&player.ID, &player.Name, &player.Email, &player.Role)
if err != nil {
log.Printf("Error retrieving player by email %s: %v", email, err)
return Player{}, err
}
return player, nil
}
func GetPlayerByName(db *sql.DB, name string) (Player, error) {
var player Player
err := db.QueryRow("SELECT id, name, email FROM players WHERE name = $1", name).Scan(&player.ID, &player.Name, &player.Email, &player.Role)
if err != nil {
log.Printf("Error retrieving player by name %s: %v", name, err)
return Player{}, err
}
return player, nil
}
// DeletePlayer deletes a player from the database by ID.
func deletePlayer(db *sql.DB, id string) error {
// Delete the player from the database
log.Printf("Deleting player with ID: %s", id)
_, err := db.Exec("DELETE FROM public.players WHERE id = $1", id)
if err != nil {
log.Printf("Error deleting player with ID %s: %v", id, err)
return err
}
log.Printf("Player with ID %s deleted successfully", id)
return nil
}
// UpdatePlayer updates an existing player in the database.
// It requires the player ID to be set in the Player struct.
func updatePlayer(db *sql.DB, player Player) error {
// Ensure the player ID is set
if player.ID == "" {
log.Printf("Player ID is required for update, but got empty ID")
return sql.ErrNoRows // or a custom error
}
log.Printf("Updating player: ID=%v, Name=%v, Email=%v", player.ID, player.Name, player.Email)
stmt := "UPDATE public.players SET name = $1, email = $2 WHERE id = $3"
_, err := db.Exec(stmt, player.Name, player.Email, player.ID)
if err != nil {
log.Printf("Error updating player in database: %v", err)
return err
}
log.Printf("Player %s updated successfully", player.Name)
return nil
}
var players_default = []Player{{
ID: "1",
Name: "John Doe",
Email: "John.Doe@example.de",
Role: "player",
Teams: []Team{
{ID: "team1"},
{ID: "team2"},
},
},
{
ID: "2",
Name: "Jane Smith",
Email: "Jane-Smith@example.de",
Role: "player",
Teams: []Team{
{ID: "team3"},
{ID: "team1"},
},
},
}

View File

@@ -4,6 +4,8 @@ import (
"net/http"
"volleyball/internal/common"
"slices"
"github.com/gin-gonic/gin"
)
@@ -17,7 +19,7 @@ var tournaments = []Tournament{
{ID: "t1", Name: "Smasher"},
{ID: "t2", Name: "Blockbuster"},
},
OrganizerId: "system-user-id",
OrganizerId: []string{"example-user"},
},
{
ID: "2",
@@ -25,7 +27,7 @@ var tournaments = []Tournament{
Location: "Hamburg",
MaxParticipants: 10,
Teams: []Team{},
OrganizerId: "other-user",
OrganizerId: []string{"example-user"},
},
}
@@ -74,7 +76,8 @@ func UpdateTournament(c *gin.Context) {
for i, t := range tournaments {
if t.ID == id {
if t.OrganizerId != userId {
isOrganizer := slices.Contains(t.OrganizerId, userId)
if !isOrganizer {
c.JSON(http.StatusForbidden, gin.H{"error": "No permission"})
return
}

View File

@@ -6,10 +6,10 @@ type Team struct {
}
type Tournament struct {
ID string `json:"id"`
Name string `json:"name"`
Location string `json:"location"`
MaxParticipants int `json:"maxParticipants"`
Teams []Team `json:"teams"`
OrganizerId string `json:"organizerId"`
ID string `json:"id"`
Name string `json:"name"`
Location string `json:"location"`
MaxParticipants int `json:"maxParticipants"`
Teams []Team `json:"teams"`
OrganizerId []string `json:"PlayerId"` // List of player IDs who are organizers
}