diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index 4575903..2165a2d 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -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) } diff --git a/backend/go.mod b/backend/go.mod index 6a106aa..f2a591c 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -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 ) diff --git a/backend/go.sum b/backend/go.sum index 7261979..f428554 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -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= diff --git a/backend/internal/database/initTables.go b/backend/internal/database/initTables.go index 324e7f8..67f7c83 100644 --- a/backend/internal/database/initTables.go +++ b/backend/internal/database/initTables.go @@ -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, diff --git a/backend/internal/player/handler_test.go b/backend/internal/player/handler_test.go new file mode 100644 index 0000000..4cc568b --- /dev/null +++ b/backend/internal/player/handler_test.go @@ -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) + } + }) +} diff --git a/backend/internal/team/handler.go b/backend/internal/team/handler.go index 03e11bc..7ba559f 100644 --- a/backend/internal/team/handler.go +++ b/backend/internal/team/handler.go @@ -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) - return - } - log.Printf("User %s (%s) requested players, but none found", 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 + } + + 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) - common.RespondError(c, http.StatusNotFound, "No Players found") } diff --git a/backend/internal/team/model.go b/backend/internal/team/model.go index 35e7240..2efb1a9 100644 --- a/backend/internal/team/model.go +++ b/backend/internal/team/model.go @@ -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:"name" sql:"VARCHAR(100)"` + OwnerUUID string `db:"owner_uuid" sql:"VARCHAR(255)" index:"true"` + Description string `db:"describtion" sql:"VARCHAR(255)"` } -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)"` +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 + } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 15b9ed7..76372d4 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -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", diff --git a/frontend/src/components/requests/TeamData.tsx b/frontend/src/components/requests/TeamData.tsx deleted file mode 100644 index cbf1102..0000000 --- a/frontend/src/components/requests/TeamData.tsx +++ /dev/null @@ -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(); -} diff --git a/frontend/src/pages/Administration/Teams.tsx b/frontend/src/pages/Administration/Teams.tsx index 69312b8..35cb96e 100644 --- a/frontend/src/pages/Administration/Teams.tsx +++ b/frontend/src/pages/Administration/Teams.tsx @@ -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([]); + const navigate = useNavigate(); useEffect(() => { // Replace with API call in production @@ -16,8 +18,10 @@ const Teams: React.FC = () => { async function load() { try { - const teams : Team[] = await getTeams(localStorage.getItem("token") || ""); - setTeams(teams); + 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 (

Teams Administration

+ diff --git a/frontend/src/pages/Administration/Users.tsx b/frontend/src/pages/Administration/Users.tsx index b93dae1..2a5edb1 100644 --- a/frontend/src/pages/Administration/Users.tsx +++ b/frontend/src/pages/Administration/Users.tsx @@ -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}`); diff --git a/frontend/src/pages/Teams.tsx b/frontend/src/pages/Teams.tsx index 81792a4..234c40d 100644 --- a/frontend/src/pages/Teams.tsx +++ b/frontend/src/pages/Teams.tsx @@ -1,140 +1,232 @@ 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([]); const [name, setName] = useState(''); - const [player2, setPlayer2] = useState(''); + const [description, setDescription] = useState(''); const [error, setError] = useState(''); const [editingTeam, setEditingTeam] = useState(null); + const [selectedTeam, setSelectedTeam] = useState(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, { - method, - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${token}`, - }, - body: JSON.stringify(body), - }); + try { + const res = await fetch(API_URL+url, { + method, + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify(body), + }); - const data = await res.json(); + const data = await res.json(); + console.log("Response data:", data); - if (res.ok) { - setName(''); - setPlayer2(''); - setEditingTeam(null); - fetchTeams(); - } else { - setError(data.error || 'Fehler beim Speichern'); + if (res.ok) { + 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) => { - const res = await fetch(`/api/teams/${id}`, { - method: 'DELETE', - headers: { Authorization: `Bearer ${token}` }, - }); + 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 ( -
+

Team Management

- {error &&

{error}

} + {error &&

{error}

} - {canCreate() && ( -
-

{editingTeam ? 'Team bearbeiten' : 'Team erstellen'}

- setName(e.target.value)} - required - /> - - setPlayer2(e.target.value)} - required={!isAdmin} - /> - - - )} - -
-

Alle Teams

-
    - {teams.map((team) => ( -
  • - {team.name} – Spieler: {team.players.join(', ')} -
    - {canEdit(team) && ( - - )} - {canDelete(team) && ( - - )} + ))} +
    +
+ +
+ {canCreate() && ( + + )} +
+ +
+ +
+ {!editingTeam && !selectedTeam && ( +

Neues Team erstellen

+ )} + {editingTeam && ( +

Team ansehen/bearbeiten: {editingTeam.Name}

+ )} + + {/* Form for creating or editing a team */} + {(!selectedTeam && canCreate()) || editingTeam ? ( +
+
+ + setName(e.target.value)} + required + />
- - ))} - + +
+ + setDescription(e.target.value)} + // required={!isAdmin} // Admin can create single-player teams maybe? + /> +
+ +
+ + {editingTeam && canDelete(editingTeam) && ( + + )} +
+ + ) : ( +

Wähle ein Team aus der Liste oben aus, um es zu bearbeiten, oder erstelle ein neues Team.

+ )} +
); }; diff --git a/frontend/src/pages/api.tsx b/frontend/src/pages/api.tsx index 6db18f2..a298b1c 100644 --- a/frontend/src/pages/api.tsx +++ b/frontend/src/pages/api.tsx @@ -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(); +} diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..8da96b2 --- /dev/null +++ b/start.sh @@ -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."