567 lines
14 KiB
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)
|
|
}
|