ADD: added initial page with login

This commit is contained in:
hwinkel
2025-05-20 22:58:31 +02:00
parent 214ab55ad2
commit a330291456
25 changed files with 1064 additions and 35 deletions

View 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"})
}

View 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
}

View 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()
}
}

View 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)
}

View File

@@ -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")

View File

@@ -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.")
}

View 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"})
}

View 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"`
}