diff --git a/assets/templates/archetype.tmpl b/assets/templates/archetype.tmpl index 00755de5959af3cc2baac9183a685766fc67255d..17e00da777e1ff4df79b187aae3b47404c98865d 100644 --- a/assets/templates/archetype.tmpl +++ b/assets/templates/archetype.tmpl @@ -60,7 +60,7 @@ class="fab fa-instagram"></i> Instagram</a></li> <li><a href="https://discord.gg/aovtr"> <i class="fab fa-discord"></i> Discord</a> - <li><a href="{{.auth_ref}}"> <i class="fas fa-user"></i> {{.auth_text}}</a> + <li><a href="{{.auth_ref}}"> <i class="fas fa-user"></i> {{.auth_text}} (Click to log out)</a> </li> </ul> </div> diff --git a/assets/templates/form.tmpl b/assets/templates/form.tmpl index 8fca64f772830ef9830c135976e59d2e7fd3a950..5eb300e26092baa82839f88cc707ed21d4ba182d 100644 --- a/assets/templates/form.tmpl +++ b/assets/templates/form.tmpl @@ -12,56 +12,57 @@ <h2>{{.tournament.Title}} <span>FORMU</span></h2> </div> <div class="contact-form"> - <form action="#"> + <form method="post" action=""> <div class="row"> {{if .team_size.A}} <div class="col-md-12"> <label> - <input name="teamName" type="text" placeholder="Takım Adınız"> + <input type="text" placeholder="Takım Adınız" name="TeamName"> </label> </div> <div class="col-md-6"> <label> - <input type="text" placeholder="Takım Kaptanınız"> + Takım Kaptanınız + <input type="text" placeholder="Takım Kaptanınız" value="{{.auth_text}}" disabled="disabled"> </label> </div> {{end}} <div class="col-md-6"> <label> - <input type="text" placeholder="Adınız"> + <input type="text" placeholder="Adınız" name="FirstName"> </label> </div> <div class="col-md-6"> <label> - <input type="text" placeholder="Soyadınız"> + <input type="text" placeholder="Soyadınız" name="LastName"> </label> </div> {{if .team_size.A}} <div class="col-md-6"> <label> - <input type="text" placeholder="Player 1 Name + UID"> + <input type="text" placeholder="Player 1 Name + UID" name="Player0"> </label> </div> {{ end }} {{if .team_size.B}} <div class="col-md-6"> <label> - <input type="text" placeholder="Player 2 Name + UID"> + <input type="text" placeholder="Player 2 Name + UID" name="Player1"> </label> </div> {{ end }} {{if .team_size.C}} <div class="col-md-6"> <label> - <input type="text" placeholder="Player 3 Name + UID"> + <input type="text" placeholder="Player 3 Name + UID" name="Player2"> </label> </div> {{ end }} {{if .team_size.D}} <div class="col-md-6"> <label> - <input type="text" placeholder="Player 4 Name + UID"> + <input type="text" placeholder="Player 4 Name + UID" name="Player3"> </label> </div> {{ end }} diff --git a/routes.go b/routes.go index 87498ec92d274dbc6608bdb044bf1ac0b845ac97..bc09d6d5590ab9c952806d39619cc8de94567e2c 100644 --- a/routes.go +++ b/routes.go @@ -35,13 +35,24 @@ func registerRoutes() { return } - // TODO: consume user stuff here - t := contextTournament(context) if t == nil { return } populateTournament(t) + if t.EnrollmentOverdue { + context.Redirect(http.StatusTemporaryRedirect, "/") + return + } + if s, err := existsTeam(t.ID, user.ID); err != nil { + log.Printf("error querying team existance for team %s in tournament %s: %s", + user.ID, t.ID.String(), err) + context.String(http.StatusInternalServerError, "Internal Server Error") + return + } else if s { + context.Redirect(http.StatusTemporaryRedirect, "/") + return + } authText, authRef := getAuthButton(user) @@ -95,8 +106,48 @@ func registerRoutes() { return } - // TODO: handle form submit - context.String(http.StatusOK, "TODO") + t := contextTournament(context) + if t == nil { + return + } + populateTournament(t) + if t.EnrollmentOverdue { + context.Redirect(http.StatusTemporaryRedirect, "/") + return + } + + if s, err := existsTeam(t.ID, user.ID); err != nil { + log.Printf("error querying team existance for team %s in tournament %s: %s", + user.ID, t.ID.String(), err) + context.String(http.StatusInternalServerError, "Internal Server Error") + return + } else if s { + context.Redirect(http.StatusFound, "/") + return + } + + var tsn int + if n, err := strconv.Atoi(t.Attributes["size"]); err != nil { + log.Printf("error parsing team size of team %s: %s", t.ID.String(), err) + context.String(http.StatusInternalServerError, "Internal Server Error") + return + } else { + tsn = n + } + + team := bindEnrollment(context, user, tsn) + if team == nil { + return + } + team.Tournament = t.ID + + if err := putTeam(team); err != nil { + log.Printf("error pushing enrollment for tournament %s: %s", t.ID.String(), err) + context.String(http.StatusInternalServerError, "Internal Server Error") + return + } + + context.Redirect(http.StatusFound, "/") }) router.GET("/admin", func(context *gin.Context) { diff --git a/tournament.go b/tournament.go index 7b6c3d8f7537a9e0e2a91bdd346a01cca170c388..34e5f4fd2910db60d1252068b2a39042f8a80b42 100644 --- a/tournament.go +++ b/tournament.go @@ -107,12 +107,16 @@ func getTournaments() ([]*tournament, error) { return tournamentsCache, nil } +func getBytesTournament(id uuid.UUID) ([]byte, error) { + return id.MarshalBinary() +} + func getTournament(id uuid.UUID) (*tournament, []byte, error) { tournamentLock.RLock() defer tournamentLock.RUnlock() var b []byte - if payload, err := id.MarshalBinary(); err != nil { + if payload, err := getBytesTournament(id); err != nil { return nil, nil, err } else { b = payload @@ -180,7 +184,7 @@ func makeTournament(t *tournament) error { func updateTournament(t *tournament) error { var b []byte - if _, p, err := getTournament(t.ID); err != nil { + if p, err := getBytesTournament(t.ID); err != nil { return err } else { b = p @@ -204,7 +208,7 @@ func updateTournament(t *tournament) error { func destroyTournament(id uuid.UUID) error { var b []byte - if _, p, err := getTournament(id); err != nil { + if p, err := getBytesTournament(id); err != nil { return err } else { b = p @@ -231,12 +235,12 @@ func contextTournament(context *gin.Context) *tournament { if config.System.Verbose { log.Printf("error parsing UUID %s: %s", context.Param("id"), err) } - context.Redirect(http.StatusTemporaryRedirect, "/admin") + context.Redirect(http.StatusTemporaryRedirect, "/") return nil } else { if t, _, err = getTournament(id); err != nil { if isNotFound(err) { - context.Redirect(http.StatusTemporaryRedirect, "/admin") + context.Redirect(http.StatusTemporaryRedirect, "/") return nil } log.Printf("error getting tournament %s: %s", id.String(), err) diff --git a/user.go b/user.go index 9626b9d6353b3c4c0348af41ef16b6e16d582880..774424be79ad3c5c87d0b79acba9ae07d6cce9a0 100644 --- a/user.go +++ b/user.go @@ -2,17 +2,229 @@ package main import ( "github.com/bwmarrin/discordgo" - "sync" + "github.com/gin-gonic/gin" + "github.com/google/uuid" + json "github.com/json-iterator/go" + "github.com/syndtr/goleveldb/leveldb" + "log" + "net/http" ) -var userLock = sync.RWMutex{} +type userEnrollment struct { + TeamName string + FirstName string + LastName string + + Player0 string + Player1 string + Player2 string + Player3 string +} + +type teamPayload struct { + Name string `json:"name,omitempty"` + SubmitterFirstName string `json:"submitter_first_name"` + SubmitterLastName string `json:"submitter_last_name"` + Captain string `json:"captain"` + Players []string `json:"players,omitempty"` + Tournament uuid.UUID `json:"tournament"` +} + +func putTeam(t *teamPayload) error { + var b []byte + if p, err := getBytesTournament(t.Tournament); err != nil { + return err + } else { + b = p + } + + var enrollments []string + if data, err := userDB.Get(b, nil); err != nil && !isNotFound(err) { + return err + } else { + if data != nil { + if err = json.Unmarshal(data, &enrollments); err != nil { + return err + } + } + enrollments = append(enrollments, t.Captain) + if data, err = json.Marshal(enrollments); err != nil { + return err + } else { + if err = userDB.Put(b, data, nil); err != nil { + return err + } + } + } + + var tournaments = make(map[string]*teamPayload) + if data, err := userDB.Get([]byte(t.Captain), nil); err != nil && !isNotFound(err) { + return err + } else { + if data != nil { + if err = json.Unmarshal(data, &tournaments); err != nil { + return err + } + } + tournaments[string(b)] = t + if data, err = json.Marshal(tournaments); err != nil { + return err + } else { + if err = userDB.Put([]byte(t.Captain), data, nil); err != nil { + return err + } + } + } + + return nil +} + +func existsTeam(t uuid.UUID, captain string) (bool, error) { + if teams, err := getTournamentTeam(t); err != nil { + if isNotFound(err) { + return false, nil + } + return false, err + } else { + for _, team := range teams { + if team == captain { + return true, nil + } + } + return false, nil + } +} + +func getTeam(t uuid.UUID, captain string) (*teamPayload, error) { + var b []byte + if p, err := getBytesTournament(t); err != nil { + return nil, err + } else { + b = p + } + + var payload = make(map[string]*teamPayload) + if data, err := userDB.Get([]byte(captain), nil); err != nil && !isNotFound(err) { + return nil, err + } else { + if data != nil { + if err = json.Unmarshal(data, &payload); err != nil { + return nil, err + } + } + if p, ok := payload[string(b)]; !ok { + return nil, leveldb.ErrNotFound + } else { + return p, nil + } + } +} + +func getTournamentTeam(t uuid.UUID) ([]string, error) { + var b []byte + if p, err := getBytesTournament(t); err != nil { + return nil, err + } else { + b = p + } + + var payload []string + if data, err := userDB.Get(b, nil); err != nil && !isNotFound(err) { + return nil, err + } else { + if data != nil { + if err = json.Unmarshal(data, &payload); err != nil { + return nil, err + } + } + return payload, nil + } +} func getAuthButton(user *discordgo.User) (authText, authRef string) { authText = "Login" authRef = "/auth/login" if user != nil { - authText = user.Username + " (Click to logout)" + authText = user.Username authRef = "/auth/logout" } return } + +func validateEnrollment(payload *userEnrollment) bool { + return len(payload.TeamName) <= 64 && + len(payload.FirstName) > 0 && len(payload.FirstName) <= 64 && + len(payload.LastName) > 0 && len(payload.LastName) <= 64 && + len(payload.Player0) <= 64 && + len(payload.Player1) <= 64 && + len(payload.Player2) <= 64 && + len(payload.Player3) <= 64 +} + +func bindEnrollment(context *gin.Context, user *discordgo.User, tsn int) *teamPayload { + payload := userEnrollment{} + if err := context.Bind(&payload); err != nil { + if config.System.Verbose { + log.Printf("error binding enrollment form: %s", err) + } + context.String(http.StatusBadRequest, "Bad Request") + return nil + } + + if !validateEnrollment(&payload) { + if config.System.Verbose { + log.Print("invalid data submitted to enrollment form") + log.Print(payload) + } + context.String(http.StatusBadRequest, "Bad Request") + return nil + } + + var players []string + if tsn > 0 { + if len(payload.TeamName) < 0 { + context.String(http.StatusBadRequest, "Bad Request") + return nil + } + players = make([]string, tsn) + + if len(payload.Player0) > 0 { + players[0] = payload.Player0 + } else { + context.String(http.StatusBadRequest, "Bad Request") + return nil + } + } + if tsn > 1 { + if len(payload.Player1) > 0 { + players[1] = payload.Player1 + } else { + context.String(http.StatusBadRequest, "Bad Request") + return nil + } + } + if tsn > 2 { + if len(payload.Player2) > 0 { + players[2] = payload.Player2 + } else { + context.String(http.StatusBadRequest, "Bad Request") + return nil + } + } + if tsn > 3 { + if len(payload.Player3) > 0 { + players[3] = payload.Player3 + } else { + context.String(http.StatusBadRequest, "Bad Request") + return nil + } + } + + return &teamPayload{ + Name: payload.TeamName, + SubmitterFirstName: payload.FirstName, + SubmitterLastName: payload.LastName, + Captain: user.ID, + Players: players, + } +}