package api import ( "fmt" "log" "math/rand" "net/http" "strconv" "strings" "time" "srs.epita.fr/fic-server/admin/pki" "srs.epita.fr/fic-server/libfic" "github.com/gin-gonic/gin" ) func declareTeamsRoutes(router *gin.RouterGroup) { router.GET("/teams.json", func(c *gin.Context) { teams, err := fic.ExportTeams(false) if err != nil { log.Println("Unable to ExportTeams:", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during teams export."}) return } c.JSON(http.StatusOK, teams) }) router.GET("/teams-members.json", func(c *gin.Context) { teams, err := fic.ExportTeams(true) if err != nil { log.Println("Unable to ExportTeams:", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during teams export."}) return } c.JSON(http.StatusOK, teams) }) router.GET("/teams-associations.json", allAssociations) router.GET("/teams-binding", bindingTeams) router.GET("/teams-nginx", nginxGenTeams) router.POST("/refine_colors", refineTeamsColors) router.POST("/disableinactiveteams", disableInactiveTeams) router.POST("/enableallteams", enableAllTeams) router.GET("/teams-members-nginx", nginxGenMember) router.GET("/teams-tries.json", func(c *gin.Context) { tries, err := fic.GetTries(nil, nil) if err != nil { log.Println("Unable to GetTries:", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieves tries."}) return } c.JSON(http.StatusOK, tries) }) router.GET("/teams", func(c *gin.Context) { teams, err := fic.GetTeams() if err != nil { log.Println("Unable to GetTeams:", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during teams listing."}) return } c.JSON(http.StatusOK, teams) }) router.POST("/teams", createTeam) apiTeamsRoutes := router.Group("/teams/:tid") apiTeamsRoutes.Use(TeamHandler) apiTeamsRoutes.GET("/", func(c *gin.Context) { c.JSON(http.StatusOK, c.MustGet("team").(*fic.Team)) }) apiTeamsRoutes.PUT("/", updateTeam) apiTeamsRoutes.POST("/", addTeamMember) apiTeamsRoutes.DELETE("/", deleteTeam) apiTeamsRoutes.GET("/score-grid.json", func(c *gin.Context) { team := c.MustGet("team").(*fic.Team) sg, err := team.ScoreGrid() if err != nil { log.Printf("Unable to get ScoreGrid(tid=%d): %s", team.Id, err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during score grid calculation."}) return } c.JSON(http.StatusOK, sg) }) apiTeamsPublicRoutes := router.Group("/teams/:tid") apiTeamsPublicRoutes.Use(TeamPublicHandler) apiTeamsPublicRoutes.GET("/my.json", func(c *gin.Context) { var team *fic.Team if t, ok := c.Get("team"); ok && t != nil { team = t.(*fic.Team) } tfile, err := fic.MyJSONTeam(team, true) if err != nil { log.Println("Unable to get MyJSONTeam:", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during team JSON generation."}) return } c.JSON(http.StatusOK, tfile) }) apiTeamsPublicRoutes.GET("/wait.json", func(c *gin.Context) { var team *fic.Team if t, ok := c.Get("team"); ok && t != nil { team = t.(*fic.Team) } tfile, err := fic.MyJSONTeam(team, false) if err != nil { log.Println("Unable to get MyJSONTeam:", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during team JSON generation."}) return } c.JSON(http.StatusOK, tfile) }) apiTeamsPublicRoutes.GET("/stats.json", func(c *gin.Context) { var team *fic.Team if t, ok := c.Get("team"); ok && t != nil { team = t.(*fic.Team) } if team != nil { stats, err := team.GetStats() if err != nil { log.Println("Unable to get GetStats:", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during stats calculation."}) return } c.JSON(http.StatusOK, stats) } else { stats, err := fic.GetTeamsStats(nil) if err != nil { log.Println("Unable to get GetTeamsStats:", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during global stats calculation."}) return } c.JSON(http.StatusOK, stats) } }) apiTeamsRoutes.GET("/history.json", func(c *gin.Context) { team := c.MustGet("team").(*fic.Team) history, err := team.GetHistory() if err != nil { log.Println("Unable to get GetHistory:", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during history calculation."}) return } c.JSON(http.StatusOK, history) }) apiTeamsRoutes.PATCH("/history.json", updateHistory) apiTeamsRoutes.DELETE("/history.json", delHistory) apiTeamsPublicRoutes.GET("/tries", func(c *gin.Context) { team := c.MustGet("team").(*fic.Team) tries, err := fic.GetTries(team, nil) if err != nil { log.Println("Unable to GetTries:", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during tries calculation."}) return } c.JSON(http.StatusOK, tries) }) apiTeamsRoutes.GET("/members", func(c *gin.Context) { team := c.MustGet("team").(*fic.Team) members, err := team.GetMembers() if err != nil { log.Println("Unable to GetMembers:", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during members retrieval."}) return } c.JSON(http.StatusOK, members) }) apiTeamsRoutes.POST("/members", addTeamMember) apiTeamsRoutes.PUT("/members", setTeamMember) declareTeamsPasswordRoutes(apiTeamsRoutes) declareTeamClaimsRoutes(apiTeamsRoutes) declareTeamCertificateRoutes(apiTeamsRoutes) } func TeamHandler(c *gin.Context) { tid, err := strconv.ParseInt(string(c.Params.ByName("tid")), 10, 64) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid team identifier"}) return } team, err := fic.GetTeam(tid) if err != nil { c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Team not found"}) return } c.Set("team", team) c.Next() } func TeamPublicHandler(c *gin.Context) { tid, err := strconv.ParseInt(string(c.Params.ByName("tid")), 10, 64) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid team identifier"}) return } if tid != 0 { team, err := fic.GetTeam(tid) if err != nil { c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Team not found"}) return } c.Set("team", team) } else { c.Set("team", nil) } c.Next() } func nginxGenTeams(c *gin.Context) { teams, err := fic.GetTeams() if err != nil { log.Println("Unable to GetTeams:", err.Error()) c.AbortWithError(http.StatusInternalServerError, err) return } ret := "" for _, team := range teams { ret += fmt.Sprintf(" if ($remote_user = \"%s\") { set $team \"%d\"; }\n", strings.ToLower(team.Name), team.Id) } c.String(http.StatusOK, ret) } func nginxGenMember(c *gin.Context) { teams, err := fic.GetTeams() if err != nil { log.Println("Unable to GetTeams:", err.Error()) c.AbortWithError(http.StatusInternalServerError, err) return } ret := "" for _, team := range teams { if members, err := team.GetMembers(); err == nil { for _, member := range members { ret += fmt.Sprintf(" if ($remote_user = \"%s\") { set $team \"%d\"; }\n", member.Nickname, team.Id) } } else { c.AbortWithError(http.StatusInternalServerError, err) return } } c.String(http.StatusOK, ret) } func bindingTeams(c *gin.Context) { teams, err := fic.GetTeams() if err != nil { log.Println("Unable to GetTeams:", err.Error()) c.AbortWithError(http.StatusInternalServerError, err) return } ret := "" for _, team := range teams { if members, err := team.GetMembers(); err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } else { var mbs []string for _, member := range members { mbs = append(mbs, fmt.Sprintf("%s %s", member.Firstname, member.Lastname)) } ret += fmt.Sprintf("%d;%s;%s\n", team.Id, team.Name, strings.Join(mbs, ";")) } } c.String(http.StatusOK, ret) } func allAssociations(c *gin.Context) { teams, err := fic.GetTeams() if err != nil { log.Println("Unable to GetTeams:", err.Error()) c.AbortWithError(http.StatusInternalServerError, err) return } var ret []string for _, team := range teams { assocs, err := pki.GetTeamAssociations(TeamsDir, team.Id) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } for _, a := range assocs { ret = append(ret, a) } } c.JSON(http.StatusOK, ret) } func createTeam(c *gin.Context) { var ut fic.Team err := c.ShouldBindJSON(&ut) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) return } if ut.Color == 0 { ut.Color = fic.HSL{ H: rand.Float64(), S: 1, L: 0.5, }.ToRGB() } team, err := fic.CreateTeam(strings.TrimSpace(ut.Name), ut.Color, ut.ExternalId) if err != nil { log.Println("Unable to CreateTeam:", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during team creation."}) return } c.JSON(http.StatusOK, team) } func updateTeam(c *gin.Context) { team := c.MustGet("team").(*fic.Team) var ut fic.Team err := c.ShouldBindJSON(&ut) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) return } ut.Id = team.Id if ut.Password != nil && *ut.Password == "" { ut.Password = nil } _, err = ut.Update() if err != nil { log.Println("Unable to updateTeam:", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during team updating."}) return } c.JSON(http.StatusOK, ut) } func refineTeamsColors(c *gin.Context) { teams, err := fic.GetTeams() if err != nil { log.Println("Unable to GetTeams:", err.Error()) c.AbortWithError(http.StatusInternalServerError, err) return } for i, team := range teams { team.Color = fic.HSL{ H: float64(i)/float64(len(teams)) - 0.2, S: float64(1) / float64(1+i%2), L: 0.25 + float64(0.5)/float64(1+i%3), }.ToRGB() _, err = team.Update() if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } } c.JSON(http.StatusOK, teams) } func disableInactiveTeams(c *gin.Context) { teams, err := fic.GetTeams() if err != nil { log.Println("Unable to GetTeams:", err.Error()) c.AbortWithError(http.StatusInternalServerError, err) return } for _, team := range teams { var serials []uint64 serials, err = pki.GetTeamSerials(TeamsDir, team.Id) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } var assocs []string assocs, err = pki.GetTeamAssociations(TeamsDir, team.Id) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } if len(serials) == 0 && len(assocs) == 0 { if team.Active { team.Active = false team.Update() } } else if !team.Active { team.Active = true team.Update() } } c.JSON(http.StatusOK, true) } func enableAllTeams(c *gin.Context) { teams, err := fic.GetTeams() if err != nil { log.Println("Unable to GetTeams:", err.Error()) c.AbortWithError(http.StatusInternalServerError, err) return } for _, team := range teams { if !team.Active { team.Active = true team.Update() } } c.JSON(http.StatusOK, true) } func deleteTeam(c *gin.Context) { team := c.MustGet("team").(*fic.Team) assocs, err := pki.GetTeamAssociations(TeamsDir, team.Id) if err != nil { log.Printf("Unable to GetTeamAssociations(tid=%d): %s", team.Id, err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to retrieve team association."}) return } for _, assoc := range assocs { err = pki.DeleteTeamAssociation(TeamsDir, assoc) if err != nil { log.Printf("Unable to DeleteTeamAssociation(assoc=%s): %s", assoc, err.Error()) return } } _, err = team.Delete() if err != nil { log.Println("Unable to deleteTeam:", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during team deletion."}) return } c.JSON(http.StatusOK, true) } func addTeamMember(c *gin.Context) { team := c.MustGet("team").(*fic.Team) var members []fic.Member err := c.ShouldBindJSON(&members) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) return } for _, member := range members { _, err := team.AddMember(strings.TrimSpace(member.Firstname), strings.TrimSpace(member.Lastname), strings.TrimSpace(member.Nickname), strings.TrimSpace(member.Company)) if err != nil { log.Println("Unable to AddMember:", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during member creation."}) return } } mmbrs, err := team.GetMembers() if err != nil { log.Println("Unable to retrieve members list:", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve members list."}) return } c.JSON(http.StatusOK, mmbrs) } func setTeamMember(c *gin.Context) { team := c.MustGet("team").(*fic.Team) team.ClearMembers() addTeamMember(c) } type uploadedHistory struct { Kind string Time time.Time Primary *int64 Secondary *int64 Coefficient float32 } func updateHistory(c *gin.Context) { team := c.MustGet("team").(*fic.Team) var uh uploadedHistory err := c.ShouldBindJSON(&uh) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) return } var givenId int64 if uh.Secondary != nil { givenId = *uh.Secondary } else if uh.Primary != nil { givenId = *uh.Primary } _, err = team.UpdateHistoryCoeff(uh.Kind, uh.Time, givenId, uh.Coefficient) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to update this history line: %s", err.Error())}) return } c.JSON(http.StatusOK, true) } func delHistory(c *gin.Context) { team := c.MustGet("team").(*fic.Team) var uh uploadedHistory err := c.ShouldBindJSON(&uh) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) return } _, err = team.DelHistoryItem(uh.Kind, uh.Time, uh.Primary, uh.Secondary) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to delete this history line: %s", err.Error())}) return } c.JSON(http.StatusOK, true) }