ADD: added team management
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
"volleyball/internal/auth"
|
||||
"volleyball/internal/database"
|
||||
"volleyball/internal/player"
|
||||
"volleyball/internal/team"
|
||||
"volleyball/internal/tournament"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
@@ -29,25 +32,49 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
r := gin.Default()
|
||||
router := gin.Default()
|
||||
// 2. CORS-Konfiguration
|
||||
// Lese die Frontend-URL aus den Umgebungsvariablen
|
||||
frontendURL := os.Getenv("FRONTEND_URL")
|
||||
|
||||
// Lokaler Fallback (wichtig für die Entwicklung)
|
||||
allowedOrigins := []string{
|
||||
"http://localhost:3000", // Gängiger React-Dev-Port
|
||||
// "http://localhost:5173", // Gängiger Vite-Dev-Port
|
||||
}
|
||||
|
||||
if frontendURL != "" {
|
||||
allowedOrigins = append(allowedOrigins, frontendURL)
|
||||
fmt.Printf("CORS: Erlaubte Produktiv-URL hinzugefügt: %s\n", frontendURL)
|
||||
} else {
|
||||
log.Println("ACHTUNG: FRONTEND_URL fehlt in den Umgebungsvariablen. Nur lokale URLs erlaubt.")
|
||||
}
|
||||
|
||||
// CORS
|
||||
r.Use(cors.New(cors.Config{
|
||||
AllowOrigins: []string{"http://localhost:3000"},
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "DELETE"},
|
||||
AllowHeaders: []string{"Authorization", "Content-Type"},
|
||||
// Konfiguriere die CORS-Middleware
|
||||
config := cors.Config{
|
||||
// Setze die erlaubten Ursprünge (deine React-URLs)
|
||||
AllowOrigins: allowedOrigins,
|
||||
// Erlaube die notwendigen HTTP-Methoden
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"},
|
||||
// Erlaube Header (z.B. für JSON und Authentifizierung)
|
||||
AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization"},
|
||||
// Erlaube Cookies und Credentials (falls du Tokens oder Sessions nutzt)
|
||||
AllowCredentials: true,
|
||||
}))
|
||||
// Wie lange die Preflight-Anfrage (OPTIONS) gecacht werden darf
|
||||
MaxAge: 12 * time.Hour,
|
||||
}
|
||||
router.Use(cors.New(config))
|
||||
|
||||
// Public
|
||||
r.POST("/api/login", func(c *gin.Context) {
|
||||
router.POST("/api/login", func(c *gin.Context) {
|
||||
auth.LoginHandler(c, db.GetDB())
|
||||
})
|
||||
|
||||
r.GET("/api/tournaments", tournament.ListTournaments)
|
||||
router.GET("/api/tournaments", tournament.ListTournaments)
|
||||
|
||||
// Protected API
|
||||
api := r.Group("/api")
|
||||
api := router.Group("/api")
|
||||
api.Use(auth.AuthMiddleware())
|
||||
|
||||
api.GET("/tournaments/:id", tournament.GetTournament)
|
||||
@@ -74,12 +101,21 @@ func main() {
|
||||
// c.JSON(http.StatusOK, gin.H{"message": "Player deleted successfully"})
|
||||
})
|
||||
api.GET("/teams", func(c *gin.Context) {
|
||||
|
||||
log.Println("get Teams called")
|
||||
team.GetTeams(c, db.GetDB())
|
||||
})
|
||||
|
||||
api.POST("/teams", func(c *gin.Context) {
|
||||
log.Println("create teams called")
|
||||
team.CreateTeam(c, db.GetDB())
|
||||
})
|
||||
api.PUT("/teams/:uuid", func(c *gin.Context) {
|
||||
log.Println("update teams called")
|
||||
team.UpdateTeam(c, db.GetDB())
|
||||
})
|
||||
port := os.Getenv("PORT")
|
||||
if port == "" {
|
||||
port = "8080"
|
||||
}
|
||||
r.Run(":" + port)
|
||||
router.Run(":" + port)
|
||||
}
|
||||
|
||||
@@ -1,43 +1,52 @@
|
||||
module volleyball
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.24.3
|
||||
go 1.25.4
|
||||
|
||||
require (
|
||||
github.com/gin-contrib/cors v1.7.5
|
||||
github.com/gin-contrib/cors v1.7.6
|
||||
github.com/lib/pq v1.10.9
|
||||
)
|
||||
|
||||
require github.com/kr/text v0.2.0 // indirect
|
||||
require (
|
||||
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||
github.com/goccy/go-yaml v1.18.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.57.1 // indirect
|
||||
go.uber.org/mock v0.6.0 // indirect
|
||||
golang.org/x/mod v0.30.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.13.2 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/gin-contrib/sse v1.0.0 // indirect
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||
github.com/bytedance/sonic v1.14.2 // indirect
|
||||
github.com/bytedance/sonic/loader v0.4.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.11 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.11.0
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.28.0 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
golang.org/x/arch v0.15.0 // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
golang.org/x/net v0.38.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
github.com/ugorji/go/codec v1.3.1 // indirect
|
||||
golang.org/x/arch v0.23.0 // indirect
|
||||
golang.org/x/crypto v0.45.0
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
|
||||
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
||||
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
|
||||
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
||||
github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE=
|
||||
github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
||||
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o=
|
||||
github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -12,12 +22,20 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik=
|
||||
github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gin-contrib/cors v1.7.5 h1:cXC9SmofOrRg0w9PigwGlHG3ztswH6bqq4vJVXnvYMk=
|
||||
github.com/gin-contrib/cors v1.7.5/go.mod h1:4q3yi7xBEDDWKapjT2o1V7mScKDDr8k+jZ0fSquGoy0=
|
||||
github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY=
|
||||
github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk=
|
||||
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
|
||||
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
|
||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
||||
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
@@ -26,8 +44,12 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
||||
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
|
||||
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
@@ -37,12 +59,16 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
@@ -58,10 +84,17 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10=
|
||||
github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
@@ -74,25 +107,48 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
|
||||
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw=
|
||||
golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
|
||||
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
|
||||
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"log"
|
||||
|
||||
"volleyball/internal/player"
|
||||
"volleyball/internal/team"
|
||||
|
||||
_ "github.com/lib/pq" // Import the PostgreSQL driver
|
||||
)
|
||||
@@ -54,6 +55,8 @@ func CheckIfTablesExist(db *sql.DB) (bool, error) {
|
||||
func InitTables(d *sql.DB) error {
|
||||
CreateOrUpdateTablePG(d, "users", player.User{})
|
||||
CreateOrUpdateTablePG(d, "roles", player.Roles{})
|
||||
createTable(d, "teams", team.Team{})
|
||||
createTable(d, "teams_player", team.TeamPlayerAssociation{})
|
||||
|
||||
tables := []string{
|
||||
// player.PlayerTable,
|
||||
|
||||
136
backend/internal/player/handler_test.go
Normal file
136
backend/internal/player/handler_test.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package player
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func TestGetPlayers(t *testing.T) {
|
||||
// Set Gin to Test Mode
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
t.Run("successful retrieval", func(t *testing.T) {
|
||||
// Create a new HTTP recorder and context
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
|
||||
// Create a mock database connection
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Define the expected rows and columns
|
||||
rows := sqlmock.NewRows([]string{"uuid", "email", "username", "firstname", "lastname", "birthday", "is_active", "created_at", "role"}).
|
||||
AddRow("uuid-1", "player1@example.com", "player1", "first1", "last1", nil, true, nil, "{player}")
|
||||
|
||||
// Expect a query to be made
|
||||
mock.ExpectQuery(getPLayerWithRolesQuery).WillReturnRows(rows)
|
||||
|
||||
// Call the handler
|
||||
GetPlayers(c, db)
|
||||
|
||||
// Check the status code and response body
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected status code %d but got %d", http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
// TODO: Add assertion for the response body
|
||||
|
||||
// Ensure all expectations were met
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("there were unfulfilled expectations: %s", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("database error", func(t *testing.T) {
|
||||
// Create a new HTTP recorder and context
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
|
||||
// Create a mock database connection
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Expect a query to fail
|
||||
mock.ExpectQuery(getPLayerWithRolesQuery).WillReturnError(sql.ErrConnDone)
|
||||
|
||||
// Call the handler
|
||||
GetPlayers(c, db)
|
||||
|
||||
// Check the status code and response body
|
||||
if w.Code != http.StatusInternalServerError {
|
||||
t.Errorf("expected status code %d but got %d", http.StatusInternalServerError, w.Code)
|
||||
}
|
||||
|
||||
// Ensure all expectations were met
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("there were unfulfilled expectations: %s", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetPlayer(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
t.Run("successful retrieval", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Params = gin.Params{gin.Param{Key: "id", Value: "uuid-1"}}
|
||||
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
rows := sqlmock.NewRows([]string{"uuid", "username", "email"}).
|
||||
AddRow("uuid-1", "player1", "player1@example.com")
|
||||
|
||||
mock.ExpectQuery("SELECT id, name, email FROM users WHERE id = \\$1").WithArgs("uuid-1").WillReturnRows(rows)
|
||||
mock.ExpectQuery("SELECT role FROM roles WHERE player_id = \\$1").WithArgs("uuid-1").WillReturnRows(sqlmock.NewRows([]string{"role"}).AddRow("player"))
|
||||
|
||||
GetPlayer(c, db, "uuid-1")
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected status code %d but got %d", http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("there were unfulfilled expectations: %s", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("not found", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Params = gin.Params{gin.Param{Key: "id", Value: "uuid-not-found"}}
|
||||
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
mock.ExpectQuery("SELECT id, name, email FROM users WHERE id = \\$1").WithArgs("uuid-not-found").WillReturnError(sql.ErrNoRows)
|
||||
|
||||
GetPlayer(c, db, "uuid-not-found")
|
||||
|
||||
if w.Code != http.StatusInternalServerError {
|
||||
t.Errorf("expected status code %d but got %d", http.StatusInternalServerError, w.Code)
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("there were unfulfilled expectations: %s", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -16,16 +16,46 @@ func GetTeams(c *gin.Context, db *sql.DB) {
|
||||
teams, err := GetAllTeams(db)
|
||||
if err != nil {
|
||||
log.Printf("Error retrieving teams: %v", err)
|
||||
common.RespondError(c, http.StatusInternalServerError, "Failed to retrieve players")
|
||||
common.RespondError(c, http.StatusInternalServerError, "Failed to retrieve teams")
|
||||
return
|
||||
}
|
||||
|
||||
if len(teams) > 0 {
|
||||
log.Printf("User %s (%s) requested players", c.GetString("userId"), c.GetString("email"))
|
||||
c.JSON(http.StatusOK, teams)
|
||||
// if len(teams) > 0 {
|
||||
// return
|
||||
// }
|
||||
|
||||
// common.RespondMessage(c, "No Players found")
|
||||
}
|
||||
|
||||
func CreateTeam(c *gin.Context, db *sql.DB) {
|
||||
var team Team
|
||||
if err := c.ShouldBindJSON(&team); err != nil {
|
||||
common.RespondError(c, http.StatusBadRequest, "Invalid request payload")
|
||||
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")
|
||||
err := saveTeam(db, team)
|
||||
if err != nil {
|
||||
log.Printf("Error saving team: %v", err)
|
||||
common.RespondError(c, http.StatusBadRequest, "Error saving Team")
|
||||
}
|
||||
common.RespondSuccess(c, http.StatusOK)
|
||||
}
|
||||
|
||||
func UpdateTeam(c *gin.Context, db *sql.DB) {
|
||||
log.Println("team id: ", c.Param("uuid"))
|
||||
var team Team
|
||||
if err := c.ShouldBindJSON(&team); err != nil {
|
||||
common.RespondError(c, http.StatusBadRequest, "Error updating Team; Could not bind Params")
|
||||
return
|
||||
}
|
||||
team.UUID = c.Param("uuid")
|
||||
err := updateTeam(db, team)
|
||||
if err != nil {
|
||||
log.Printf("Error updating team: %v", err)
|
||||
common.RespondError(c, http.StatusBadRequest, "Error updating Team")
|
||||
}
|
||||
common.RespondSuccess(c, http.StatusOK)
|
||||
|
||||
}
|
||||
|
||||
@@ -2,24 +2,63 @@ package team
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Player struct {
|
||||
UUID string `db:"uuid" sql:"VARCHAR(255)" index:"true"`
|
||||
Email string `db:"email" sql:"VARCHAR(255)" index:"true"`
|
||||
Username string `db:"username" sql:"VARCHAR(100)"`
|
||||
FirstName string `db:"firstname" sql:"VARCHAR(100)"`
|
||||
LastName string `db:"lastname" sql:"VARCHAR(100)"`
|
||||
}
|
||||
|
||||
type Team struct {
|
||||
UUID string `db:"uuid" sql:"VARCHAR(255)" index:"true"`
|
||||
Name string `db:"username" sql:"VARCHAR(100)"`
|
||||
Email string `db:"email" sql:"VARCHAR(255)" index:"true"`
|
||||
PlayersUUID []Player `db:"players" sql:"JSVARCHAR(255)"`
|
||||
Name string `db:"name" sql:"VARCHAR(100)"`
|
||||
OwnerUUID string `db:"owner_uuid" sql:"VARCHAR(255)" index:"true"`
|
||||
Description string `db:"describtion" sql:"VARCHAR(255)"`
|
||||
}
|
||||
|
||||
type TeamPlayerAssociation struct {
|
||||
TeamUUID string `db:"team_uuid" sql:"VARCHAR(255)" index:"true"`
|
||||
PlayerUUID string `db:"player_uuid" sql:"VARCHAR(255)" index:"true"`
|
||||
}
|
||||
|
||||
func GetAllTeams(db *sql.DB) ([]Team, error) {
|
||||
// Implementation to retrieve all teams from the database
|
||||
return []Team{}, nil
|
||||
var teams []Team
|
||||
rows, err := db.Query("SELECT * FROM teams")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var team Team
|
||||
if err := rows.Scan(&team.UUID, &team.Name, &team.OwnerUUID, &team.Description); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
teams = append(teams, team)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return teams, nil
|
||||
}
|
||||
|
||||
func saveTeam(db *sql.DB, team Team) error {
|
||||
|
||||
stmt := "INSERT INTO public.teams (name, owner_uuid,description) VALUES ($1, $2, $3)"
|
||||
log.Printf("Generated SQL statement: %s", stmt)
|
||||
_, err := db.Exec(stmt, team.Name, team.OwnerUUID, team.Description)
|
||||
if err != nil {
|
||||
log.Printf("Error saving team to database: %v", err)
|
||||
}
|
||||
log.Printf("Team %s saved to database", team.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateTeam(db *sql.DB, team Team) error {
|
||||
stmt := "UPDATE public.teams SET name = $1, description = $2 WHERE uuid = $3"
|
||||
_, err := db.Exec(stmt, team.Name, team.Description, team.UUID)
|
||||
if err != nil {
|
||||
log.Printf("Error updating team in database: %v", err)
|
||||
return err
|
||||
}
|
||||
log.Printf("Team %s updated successfully", team.Name)
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
6
frontend/package-lock.json
generated
6
frontend/package-lock.json
generated
@@ -5134,9 +5134,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001718",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz",
|
||||
"integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==",
|
||||
"version": "1.0.30001757",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz",
|
||||
"integrity": "sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
const API_URL = 'http://localhost:8080/api';
|
||||
|
||||
export async function getTeams(token:string)
|
||||
{
|
||||
const res = await fetch(`${API_URL}/team/`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
if (!res.ok) throw new Error('Fehler beim Laden des Spielers');
|
||||
return res.json();
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Team } from "../../components/interfaces/Team";
|
||||
import { get } from "http";
|
||||
import { getTeams } from "../../components/requests/TeamData";
|
||||
import { fetchTeams } from "../api";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
|
||||
|
||||
const Teams: React.FC = () => {
|
||||
const [teams, setTeams] = useState<Team[]>([]);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
// Replace with API call in production
|
||||
@@ -16,7 +18,9 @@ const Teams: React.FC = () => {
|
||||
async function load()
|
||||
{
|
||||
try {
|
||||
const teams : Team[] = await getTeams(localStorage.getItem("token") || "");
|
||||
const teams : Team[] = await fetchTeams(localStorage.getItem("token") || "");
|
||||
console.log("Geladene Teams:", teams);
|
||||
if (teams != null)
|
||||
setTeams(teams);
|
||||
} catch (error) {
|
||||
console.error("Fehler beim Laden der Teams:", error);
|
||||
@@ -28,6 +32,8 @@ const Teams: React.FC = () => {
|
||||
return (
|
||||
<div style={{ padding: "2rem" }}>
|
||||
<h1>Teams Administration</h1>
|
||||
<button onClick={() => navigate("/teams")
|
||||
}>Create Team</button>
|
||||
<table style={{ width: "100%", borderCollapse: "collapse", marginTop: "1rem" }}>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -51,7 +51,7 @@ export default function UsersPage(): JSX.Element {
|
||||
params.set("page", String(page));
|
||||
params.set("limit", String(pageSize));
|
||||
|
||||
console.log("Fetching users with params:", params.toString());
|
||||
// console.log("Fetching users with params:", params.toString());
|
||||
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) throw new Error("No auth token found");
|
||||
@@ -62,7 +62,7 @@ export default function UsersPage(): JSX.Element {
|
||||
user.Email.toLowerCase().includes(query.toLowerCase())
|
||||
);
|
||||
setUsers(filteredData);
|
||||
console.log("Loaded users:", data);
|
||||
// console.log("Loaded users:", data);
|
||||
|
||||
|
||||
// if (!res.ok) throw new Error(`Failed to load users: ${res.status}`);
|
||||
|
||||
@@ -1,50 +1,67 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { getUserFromToken } from '../components/utils/jwt'; // Importiere die Funktion zum Decodieren des Tokens
|
||||
import { getUserFromToken } from '../components/utils/jwt';
|
||||
import { fetchTeams } from './api';
|
||||
|
||||
interface Team {
|
||||
id: string;
|
||||
name: string;
|
||||
players: string[]; // Emails oder Namen
|
||||
createdBy: string;
|
||||
UUID: string;
|
||||
Name: string;
|
||||
Players: string[];
|
||||
OwnerUUID: string;
|
||||
Description: string
|
||||
}
|
||||
|
||||
const TeamManagement = () => {
|
||||
const TeamManagement: React.FC = () => {
|
||||
const [teams, setTeams] = useState<Team[]>([]);
|
||||
const [name, setName] = useState('');
|
||||
const [player2, setPlayer2] = useState('');
|
||||
const [description, setDescription] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const [editingTeam, setEditingTeam] = useState<Team | null>(null);
|
||||
const [selectedTeam, setSelectedTeam] = useState<Team | null>(null);
|
||||
|
||||
const token = localStorage.getItem('token');
|
||||
const user = token ? getUserFromToken(token) : null;
|
||||
const isAdmin = user?.role?.includes('admin');
|
||||
const API_URL = 'http://localhost:8080/api';
|
||||
|
||||
const fetchTeams = async () => {
|
||||
const res = await fetch('/api/teams', {
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
});
|
||||
const data = await res.json();
|
||||
if (res.ok) setTeams(data.data || []);
|
||||
else setError(data.error || 'Fehler beim Laden der Teams');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (token) fetchTeams();
|
||||
}, [token]);
|
||||
load();
|
||||
}, [token]); // Add token as dependency
|
||||
|
||||
async function load() {
|
||||
if (!token) return;
|
||||
try {
|
||||
const data = await fetchTeams(token);
|
||||
// console.log("Geladene Teams:", data);
|
||||
if (data) {
|
||||
setTeams(data);
|
||||
}
|
||||
} catch (error) {
|
||||
// console.error("Fehler beim Laden der Teams:", error);
|
||||
setError('Fehler beim Laden der Teams');
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!user?.email) {
|
||||
setError('User email not found. Please log in again.');
|
||||
return;
|
||||
}
|
||||
|
||||
const body = {
|
||||
name,
|
||||
players: [user?.email, player2],
|
||||
description: description,
|
||||
owneruuid: user.userId,
|
||||
|
||||
};
|
||||
|
||||
const method = editingTeam ? 'PUT' : 'POST';
|
||||
const url = editingTeam ? `/api/teams/${editingTeam.id}` : '/api/teams';
|
||||
const url = editingTeam ? `/teams/${editingTeam.UUID}` : '/teams';
|
||||
|
||||
const res = await fetch(url, {
|
||||
try {
|
||||
const res = await fetch(API_URL+url, {
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -54,87 +71,162 @@ const TeamManagement = () => {
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
console.log("Response data:", data);
|
||||
|
||||
if (res.ok) {
|
||||
setName('');
|
||||
setPlayer2('');
|
||||
setEditingTeam(null);
|
||||
fetchTeams();
|
||||
handleCreateNewClick(); // Reset form state
|
||||
load(); // Reload teams
|
||||
} else {
|
||||
setError(data.error || 'Fehler beim Speichern');
|
||||
}
|
||||
} catch (err) {
|
||||
setError('Ein Netzwerkfehler ist aufgetreten.');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (id: string) => {
|
||||
if (!window.confirm("Are you sure you want to delete this team?")) return;
|
||||
try {
|
||||
const res = await fetch(`/api/teams/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
if (res.ok) fetchTeams();
|
||||
else setError('Löschen fehlgeschlagen');
|
||||
if (res.ok) {
|
||||
handleCreateNewClick(); // Reset form
|
||||
load(); // Refresh list
|
||||
} else {
|
||||
const data = await res.json();
|
||||
setError(data.error || 'Löschen fehlgeschlagen');
|
||||
}
|
||||
} catch (err) {
|
||||
setError('Ein Netzwerkfehler ist aufgetreten.');
|
||||
}
|
||||
};
|
||||
|
||||
const canEdit = (team: Team) => isAdmin;
|
||||
const canDelete = (team: Team) =>
|
||||
isAdmin || team.createdBy === user?.userId;
|
||||
const canDelete = (team: Team) => isAdmin || team.OwnerUUID === user?.userId;
|
||||
const canCreate = () => isAdmin || true;
|
||||
|
||||
const canCreate = () => isAdmin || true; // Normale Nutzer dürfen eigene Teams erstellen
|
||||
const handleSelectTeam = (team: Team) => {
|
||||
setSelectedTeam(team);
|
||||
setEditingTeam(team);
|
||||
setName(team.Name);
|
||||
setDescription(team.Description);
|
||||
|
||||
const currentUserEmail = user?.email;
|
||||
// description(otherPlayer || '');
|
||||
};
|
||||
|
||||
const handleCreateNewClick = () => {
|
||||
setSelectedTeam(null);
|
||||
setEditingTeam(null);
|
||||
setName('');
|
||||
setDescription('');
|
||||
setError('');
|
||||
};
|
||||
|
||||
// const myTeams = teams;
|
||||
const myTeams = teams.filter(team =>
|
||||
team.OwnerUUID === user?.userId || (user?.email && team.Players.includes(user.email))
|
||||
);
|
||||
|
||||
// console.log("meine teams ", myTeams);
|
||||
|
||||
return (
|
||||
<div className="max-w-2xl mx-auto p-6 bg-white rounded-xl shadow-md mt-6">
|
||||
<div className="max-w-4xl mx-auto p-6 bg-white rounded-xl shadow-md mt-6">
|
||||
<h2 className="text-2xl font-bold mb-4">Team Management</h2>
|
||||
{error && <p style={{ color: 'red' }}>{error}</p>}
|
||||
{error && <p className="text-red-500 bg-red-100 p-3 rounded mb-4">{error}</p>}
|
||||
|
||||
<div className="mb-6">
|
||||
<h3 className="text-xl font-semibold mb-2">Meine Teams</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{myTeams.map((team) => (
|
||||
<button
|
||||
key={team.UUID}
|
||||
onClick={() => handleSelectTeam(team)}
|
||||
className={`px-4 py-2 rounded-lg text-sm font-medium
|
||||
${selectedTeam?.UUID === team.UUID
|
||||
? 'bg-blue-600 text-white shadow-lg'
|
||||
: 'bg-gray-200 text-gray-800 hover:bg-gray-300'
|
||||
}`}
|
||||
>
|
||||
{team.Name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
{canCreate() && (
|
||||
<form onSubmit={handleSubmit} className="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-4">
|
||||
<h3>{editingTeam ? 'Team bearbeiten' : 'Team erstellen'}</h3>
|
||||
<button
|
||||
onClick={handleCreateNewClick}
|
||||
className="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600"
|
||||
>
|
||||
Neues Team erstellen
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<hr className="my-6"/>
|
||||
|
||||
<div>
|
||||
{!editingTeam && !selectedTeam && (
|
||||
<h3 className="text-xl font-semibold mb-4">Neues Team erstellen</h3>
|
||||
)}
|
||||
{editingTeam && (
|
||||
<h3 className="text-xl font-semibold mb-4">Team ansehen/bearbeiten: {editingTeam.Name}</h3>
|
||||
)}
|
||||
|
||||
{/* Form for creating or editing a team */}
|
||||
{(!selectedTeam && canCreate()) || editingTeam ? (
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label htmlFor="teamname" className="block text-sm font-medium text-gray-700">Teamname</label>
|
||||
<input
|
||||
className="border p-2 rounded"
|
||||
id="teamname"
|
||||
className="mt-1 block w-full border p-2 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
|
||||
type="text"
|
||||
placeholder="Teamname"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="description" className="block text-sm font-medium text-gray-700">Beschreibung</label>
|
||||
<input
|
||||
className="border p-2 rounded"
|
||||
type="email"
|
||||
placeholder="E-Mail von Mitspieler"
|
||||
value={player2}
|
||||
onChange={(e) => setPlayer2(e.target.value)}
|
||||
required={!isAdmin}
|
||||
id="description"
|
||||
className="mt-1 block w-full border p-2 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
|
||||
type="text"
|
||||
placeholder="Beschreibung"
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
// required={!isAdmin} // Admin can create single-player teams maybe?
|
||||
/>
|
||||
<button type="submit"
|
||||
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
|
||||
>{editingTeam ? 'Aktualisieren' : 'Erstellen'}</button>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<h3>Alle Teams</h3>
|
||||
<ul>
|
||||
{teams.map((team) => (
|
||||
<li key={team.id}>
|
||||
<strong>{team.name}</strong> – Spieler: {team.players.join(', ')}
|
||||
<div style={{ marginTop: '4px' }}>
|
||||
{canEdit(team) && (
|
||||
<button onClick={() => {
|
||||
setName(team.name);
|
||||
setPlayer2(team.players[1] || '');
|
||||
setEditingTeam(team);
|
||||
}}>
|
||||
Bearbeiten
|
||||
<div className="flex items-center gap-4">
|
||||
<button type="submit"
|
||||
className="bg-blue-600 text-white px-6 py-2 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||
>
|
||||
{editingTeam ? 'Aktualisieren' : 'Erstellen'}
|
||||
</button>
|
||||
{editingTeam && canDelete(editingTeam) && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleDelete(editingTeam.UUID)}
|
||||
className="bg-red-600 text-white px-6 py-2 rounded-md hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
|
||||
>
|
||||
Löschen
|
||||
</button>
|
||||
)}
|
||||
{canDelete(team) && (
|
||||
<button onClick={() => handleDelete(team.id)}>Löschen</button>
|
||||
</div>
|
||||
</form>
|
||||
) : (
|
||||
<p className="text-gray-500">Wähle ein Team aus der Liste oben aus, um es zu bearbeiten, oder erstelle ein neues Team.</p>
|
||||
)}
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -111,3 +111,12 @@ export async function registerTeam(id: string, team: { name: string }, token: st
|
||||
if (!res.ok) throw new Error('Team-Anmeldung fehlgeschlagen');
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function fetchTeams(token:string)
|
||||
{
|
||||
const res = await fetch(`${API_URL}/teams`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
if (!res.ok) throw new Error('Fehler beim Laden des Spielers');
|
||||
return res.json();
|
||||
}
|
||||
|
||||
15
start.sh
Executable file
15
start.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Start the frontend application
|
||||
echo "Starting frontend application..."
|
||||
cd frontend
|
||||
npm start &
|
||||
cd ..
|
||||
|
||||
# Start the backend application
|
||||
echo "Starting backend application..."
|
||||
cd backend
|
||||
go run cmd/server/main.go &
|
||||
cd ..
|
||||
|
||||
echo "All applications started."
|
||||
Reference in New Issue
Block a user