ADD: added initial page with login
This commit is contained in:
38
backend/internal/auth/handler.go
Normal file
38
backend/internal/auth/handler.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type LoginRequest struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type LoginResponse struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
func LoginHandler(c *gin.Context) {
|
||||
var req LoginRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Bad request"})
|
||||
return
|
||||
}
|
||||
|
||||
// Systemnutzer
|
||||
if req.Email == "systemuser@example.com" {
|
||||
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})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})
|
||||
}
|
||||
47
backend/internal/auth/jwt.go
Normal file
47
backend/internal/auth/jwt.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
)
|
||||
|
||||
var jwtSecret = []byte("supersecret")
|
||||
|
||||
type Claims struct {
|
||||
UserID string
|
||||
Email string
|
||||
Role string
|
||||
}
|
||||
|
||||
func CreateJWT(userID, email, role string, duration time.Duration) (string, error) {
|
||||
claims := jwt.MapClaims{
|
||||
"userId": userID,
|
||||
"email": email,
|
||||
"role": role,
|
||||
"exp": time.Now().Add(duration).Unix(),
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
return token.SignedString(jwtSecret)
|
||||
}
|
||||
|
||||
func ParseJWT(tokenStr string) (*Claims, error) {
|
||||
token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
|
||||
return jwtSecret, nil
|
||||
})
|
||||
if err != nil || !token.Valid {
|
||||
return nil, errors.New("invalid token")
|
||||
}
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid claims")
|
||||
}
|
||||
|
||||
return &Claims{
|
||||
UserID: claims["userId"].(string),
|
||||
Email: claims["email"].(string),
|
||||
Role: claims["role"].(string),
|
||||
}, nil
|
||||
}
|
||||
30
backend/internal/auth/middleware.go
Normal file
30
backend/internal/auth/middleware.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func AuthMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
if !strings.HasPrefix(authHeader, "Bearer ") {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Missing token"})
|
||||
return
|
||||
}
|
||||
tokenStr := strings.TrimPrefix(authHeader, "Bearer ")
|
||||
|
||||
claims, err := ParseJWT(tokenStr)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("userId", claims.UserID)
|
||||
c.Set("email", claims.Email)
|
||||
c.Set("role", claims.Role)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
49
backend/internal/common/response.go
Normal file
49
backend/internal/common/response.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// APIResponse strukturiert alle erfolgreichen Antworten
|
||||
type APIResponse struct {
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// APIError strukturiert Fehlerantworten
|
||||
type APIError struct {
|
||||
Error string `json:"error"`
|
||||
Details string `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
// RespondSuccess sendet 200 OK mit Daten
|
||||
func RespondSuccess(c *gin.Context, data interface{}) {
|
||||
c.JSON(http.StatusOK, APIResponse{
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// RespondMessage sendet 200 OK mit einer Nachricht
|
||||
func RespondMessage(c *gin.Context, message string) {
|
||||
c.JSON(http.StatusOK, APIResponse{
|
||||
Message: message,
|
||||
})
|
||||
}
|
||||
|
||||
// RespondCreated sendet 201 Created mit Daten
|
||||
func RespondCreated(c *gin.Context, data interface{}) {
|
||||
c.JSON(http.StatusCreated, APIResponse{
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// RespondError sendet einen Fehler mit Statuscode und Nachricht
|
||||
func RespondError(c *gin.Context, code int, message string, details ...string) {
|
||||
errResp := APIError{Error: message}
|
||||
if len(details) > 0 {
|
||||
errResp.Details = details[0]
|
||||
}
|
||||
c.AbortWithStatusJSON(code, errResp)
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
_ "github.com/lib/pq" // Import the PostgreSQL driver
|
||||
)
|
||||
|
||||
type database struct {
|
||||
@@ -35,12 +37,14 @@ func (d *database) Connect() error {
|
||||
db, err := sql.Open("postgres", psqlInfo)
|
||||
// Open a new database connection
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open database: %w", err)
|
||||
panic("failed to open database: " + err.Error())
|
||||
// return fmt.Errorf("failed to open database: %w", err)
|
||||
}
|
||||
|
||||
// Test the connection
|
||||
if err := db.Ping(); err != nil {
|
||||
return fmt.Errorf("failed to ping database: %w", err)
|
||||
panic("failed to ping database: " + err.Error())
|
||||
// return fmt.Errorf("failed to ping database: %w", err)
|
||||
}
|
||||
|
||||
log.Println("Connected to the database successfully")
|
||||
|
||||
@@ -1 +1,32 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
_ "github.com/lib/pq" // Import the PostgreSQL driver
|
||||
)
|
||||
|
||||
const playerTable = `
|
||||
CREATE TABLE IF NOT EXISTS players (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
age INT NOT NULL,
|
||||
birthday DATE NOT NULL
|
||||
);
|
||||
`
|
||||
|
||||
func InitTables(db *sql.DB) {
|
||||
tables := []string{
|
||||
playerTable,
|
||||
}
|
||||
|
||||
for _, table := range tables {
|
||||
if _, err := db.Exec(table); err != nil {
|
||||
log.Fatalf("Error creating table: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("Tables initialized successfully.")
|
||||
}
|
||||
|
||||
89
backend/internal/tournament/handler.go
Normal file
89
backend/internal/tournament/handler.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package tournament
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"volleyball/internal/common"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var tournaments = []Tournament{
|
||||
{
|
||||
ID: "1",
|
||||
Name: "Beach Cup",
|
||||
Location: "Berlin",
|
||||
MaxParticipants: 8,
|
||||
Teams: []Team{
|
||||
{ID: "t1", Name: "Smasher"},
|
||||
{ID: "t2", Name: "Blockbuster"},
|
||||
},
|
||||
OrganizerId: "system-user-id",
|
||||
},
|
||||
{
|
||||
ID: "2",
|
||||
Name: "City Open",
|
||||
Location: "Hamburg",
|
||||
MaxParticipants: 10,
|
||||
Teams: []Team{},
|
||||
OrganizerId: "other-user",
|
||||
},
|
||||
}
|
||||
|
||||
func ListTournaments(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, tournaments)
|
||||
}
|
||||
|
||||
func GetTournament(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
for _, t := range tournaments {
|
||||
if t.ID == id {
|
||||
c.JSON(http.StatusOK, t)
|
||||
return
|
||||
}
|
||||
}
|
||||
common.RespondError(c, http.StatusNotFound, "Tournament not found")
|
||||
}
|
||||
|
||||
func JoinTournament(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
var team Team
|
||||
if err := c.ShouldBindJSON(&team); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid team data"})
|
||||
return
|
||||
}
|
||||
|
||||
for i, t := range tournaments {
|
||||
if t.ID == id {
|
||||
tournaments[i].Teams = append(tournaments[i].Teams, team)
|
||||
c.JSON(http.StatusOK, tournaments[i])
|
||||
return
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Tournament not found"})
|
||||
}
|
||||
|
||||
func UpdateTournament(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
userId := c.GetString("userId")
|
||||
|
||||
var updated Tournament
|
||||
if err := c.ShouldBindJSON(&updated); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid data"})
|
||||
return
|
||||
}
|
||||
|
||||
for i, t := range tournaments {
|
||||
if t.ID == id {
|
||||
if t.OrganizerId != userId {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "No permission"})
|
||||
return
|
||||
}
|
||||
tournaments[i].Name = updated.Name
|
||||
tournaments[i].Location = updated.Location
|
||||
tournaments[i].MaxParticipants = updated.MaxParticipants
|
||||
c.JSON(http.StatusOK, tournaments[i])
|
||||
return
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Tournament not found"})
|
||||
}
|
||||
15
backend/internal/tournament/model.go
Normal file
15
backend/internal/tournament/model.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package tournament
|
||||
|
||||
type Team struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
Reference in New Issue
Block a user