server/admin/api/team.go

567 lines
14 KiB
Go

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