641 lines
17 KiB
Go
641 lines
17 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"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)
|
|
|
|
// Import teams from cyberrange
|
|
router.POST("/cyberrange-teams.json", importTeamsFromCyberrange)
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
type teamAssociation struct {
|
|
Association string `json:"association"`
|
|
TeamId int64 `json:"team_id"`
|
|
}
|
|
|
|
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 []teamAssociation
|
|
|
|
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, teamAssociation{a, team.Id})
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, ret)
|
|
}
|
|
|
|
func importTeamsFromCyberrange(c *gin.Context) {
|
|
file, err := c.FormFile("file")
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"errmsg": "Failed to get file: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
src, err := file.Open()
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"errmsg": "Failed to open file: " + err.Error()})
|
|
return
|
|
}
|
|
defer src.Close()
|
|
|
|
var ut []fic.CyberrangeTeam
|
|
err = json.NewDecoder(src).Decode(&fic.CyberrangeAPIResponse{Data: &ut})
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
|
return
|
|
}
|
|
|
|
teams, err := fic.GetTeams()
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Impossible de récupérer la liste des équipes actuelles: %s", err.Error())})
|
|
return
|
|
}
|
|
|
|
for _, crteam := range ut {
|
|
var exist_team *fic.Team
|
|
for _, team := range teams {
|
|
if team.Name == crteam.Name && team.ExternalId == crteam.UUID {
|
|
exist_team = team
|
|
break
|
|
}
|
|
}
|
|
|
|
if exist_team != nil {
|
|
exist_team.Name = crteam.Name
|
|
exist_team.ExternalId = crteam.UUID
|
|
_, err = exist_team.Update()
|
|
} else {
|
|
exist_team, err = fic.CreateTeam(crteam.Name, fic.RandomColor().ToRGB(), crteam.UUID)
|
|
}
|
|
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Impossible d'ajouter/de modifier l'équipe %v: %s", crteam, err.Error())})
|
|
return
|
|
}
|
|
|
|
// Import members
|
|
if c.DefaultQuery("nomembers", "0") != "" && len(crteam.Members) > 0 {
|
|
exist_team.ClearMembers()
|
|
|
|
for _, member := range crteam.Members {
|
|
_, err = exist_team.AddMember(member.Name, "", member.Nickname, exist_team.Name)
|
|
if err != nil {
|
|
log.Printf("Unable to add member %q to team %s (tid=%d): %s", member.UUID, exist_team.Name, exist_team.Id, err.Error())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
teams, err = fic.GetTeams()
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Impossible de récupérer la liste des équipes après import: %s", err.Error())})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, teams)
|
|
}
|
|
|
|
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.RandomColor().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)
|
|
}
|