2016-12-08 08:12:18 +00:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2022-05-16 09:38:46 +00:00
|
|
|
"log"
|
|
|
|
"net/http"
|
2022-05-01 20:05:26 +00:00
|
|
|
"path"
|
2016-12-08 08:12:18 +00:00
|
|
|
"strconv"
|
|
|
|
|
2017-12-08 20:01:42 +00:00
|
|
|
"srs.epita.fr/fic-server/admin/sync"
|
2016-12-08 08:12:18 +00:00
|
|
|
"srs.epita.fr/fic-server/libfic"
|
2021-11-13 01:00:24 +00:00
|
|
|
"srs.epita.fr/fic-server/settings"
|
2016-12-15 23:51:56 +00:00
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
"github.com/gin-gonic/gin"
|
2016-12-08 08:12:18 +00:00
|
|
|
)
|
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
func declareThemesRoutes(router *gin.RouterGroup) {
|
|
|
|
router.GET("/themes", listThemes)
|
|
|
|
router.POST("/themes", createTheme)
|
|
|
|
router.GET("/themes.json", exportThemes)
|
|
|
|
router.GET("/session-forensic.yaml", func(c *gin.Context) {
|
2022-05-01 20:05:26 +00:00
|
|
|
if s, err := settings.ReadSettings(path.Join(settings.SettingsDir, settings.SettingsFile)); err != nil {
|
2022-05-16 09:38:46 +00:00
|
|
|
log.Printf("Unable to ReadSettings: %s", err.Error())
|
|
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during settings reading."})
|
|
|
|
return
|
2022-05-24 19:54:45 +00:00
|
|
|
|
|
|
|
} else if challengeinfo, err := sync.GetFileContent(sync.GlobalImporter, "challenge.json"); err != nil {
|
|
|
|
log.Println("Unable to retrieve challenge.json:", err.Error())
|
|
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to retrive challenge.json: %s", err.Error())})
|
|
|
|
return
|
|
|
|
} else if ch, err := settings.ReadChallengeInfo(challengeinfo); err != nil {
|
2022-05-16 09:38:46 +00:00
|
|
|
log.Printf("Unable to ReadChallengeInfo: %s", err.Error())
|
|
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during challenge info reading."})
|
|
|
|
return
|
|
|
|
} else if sf, err := fic.GenZQDSSessionFile(ch, s); err != nil {
|
|
|
|
log.Printf("Unable to GenZQDSSessionFile: %s", err.Error())
|
|
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during session file generation."})
|
|
|
|
return
|
2022-05-01 20:05:26 +00:00
|
|
|
} else {
|
2022-05-16 09:38:46 +00:00
|
|
|
c.JSON(http.StatusOK, sf)
|
2022-05-01 20:05:26 +00:00
|
|
|
}
|
2022-05-16 09:38:46 +00:00
|
|
|
})
|
|
|
|
router.GET("/files-bindings", bindingFiles)
|
2016-12-15 23:51:56 +00:00
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
apiThemesRoutes := router.Group("/themes/:thid")
|
|
|
|
apiThemesRoutes.Use(ThemeHandler)
|
|
|
|
apiThemesRoutes.GET("", showTheme)
|
|
|
|
apiThemesRoutes.PUT("", updateTheme)
|
|
|
|
apiThemesRoutes.DELETE("", deleteTheme)
|
2016-12-08 08:12:18 +00:00
|
|
|
|
2024-03-12 09:38:41 +00:00
|
|
|
apiThemesRoutes.GET("/exercices_stats.json", getThemedExercicesStats)
|
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
declareExercicesRoutes(apiThemesRoutes)
|
2017-12-08 20:01:42 +00:00
|
|
|
|
|
|
|
// Remote
|
2022-05-16 09:38:46 +00:00
|
|
|
router.GET("/remote/themes", sync.ApiListRemoteThemes)
|
|
|
|
router.GET("/remote/themes/:thid", sync.ApiGetRemoteTheme)
|
|
|
|
router.GET("/remote/themes/:thid/exercices", sync.ApiListRemoteExercices)
|
|
|
|
}
|
2021-11-13 01:00:24 +00:00
|
|
|
|
2022-05-24 19:25:51 +00:00
|
|
|
type Theme struct {
|
|
|
|
*fic.Theme
|
|
|
|
ForgeLink string `json:"forge_link,omitempty"`
|
|
|
|
}
|
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
func ThemeHandler(c *gin.Context) {
|
|
|
|
thid, err := strconv.ParseInt(string(c.Params.ByName("thid")), 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid theme identifier"})
|
|
|
|
return
|
|
|
|
}
|
2021-11-13 01:00:24 +00:00
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
theme, err := fic.GetTheme(thid)
|
|
|
|
if err != nil {
|
|
|
|
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Theme not found"})
|
|
|
|
return
|
|
|
|
}
|
2021-11-13 01:00:24 +00:00
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
c.Set("theme", theme)
|
2021-11-13 01:00:24 +00:00
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
c.Next()
|
2018-01-23 00:00:24 +00:00
|
|
|
}
|
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
func fixAllURLIds(c *gin.Context) {
|
2018-01-23 00:00:24 +00:00
|
|
|
nbFix := 0
|
|
|
|
if themes, err := fic.GetThemes(); err == nil {
|
|
|
|
for _, theme := range themes {
|
|
|
|
if theme.FixURLId() {
|
|
|
|
theme.Update()
|
|
|
|
nbFix += 1
|
|
|
|
}
|
|
|
|
|
|
|
|
if exercices, err := theme.GetExercices(); err == nil {
|
|
|
|
for _, exercice := range exercices {
|
|
|
|
if exercice.FixURLId() {
|
|
|
|
exercice.Update()
|
|
|
|
nbFix += 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
c.JSON(http.StatusOK, nbFix)
|
2016-12-08 08:12:18 +00:00
|
|
|
}
|
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
func bindingFiles(c *gin.Context) {
|
|
|
|
files, err := fic.GetFiles()
|
|
|
|
if err != nil {
|
|
|
|
c.AbortWithError(http.StatusInternalServerError, err)
|
|
|
|
return
|
2016-12-08 08:12:18 +00:00
|
|
|
}
|
2022-05-16 09:38:46 +00:00
|
|
|
|
|
|
|
ret := ""
|
|
|
|
for _, file := range files {
|
|
|
|
ret += fmt.Sprintf("%s;%s\n", file.GetOrigin(), file.Path)
|
|
|
|
}
|
|
|
|
|
|
|
|
c.String(http.StatusOK, ret)
|
2016-12-08 08:12:18 +00:00
|
|
|
}
|
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
func listThemes(c *gin.Context) {
|
|
|
|
themes, err := fic.GetThemes()
|
|
|
|
if err != nil {
|
|
|
|
log.Println("Unable to listThemes:", err.Error())
|
|
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to list themes."})
|
|
|
|
return
|
|
|
|
}
|
2016-12-08 08:12:18 +00:00
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
c.JSON(http.StatusOK, themes)
|
2016-12-08 08:12:18 +00:00
|
|
|
}
|
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
func exportThemes(c *gin.Context) {
|
|
|
|
themes, err := fic.ExportThemes()
|
|
|
|
if err != nil {
|
|
|
|
log.Println("Unable to exportthemes:", err.Error())
|
|
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to export themes."})
|
|
|
|
return
|
|
|
|
}
|
2016-12-08 08:12:18 +00:00
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
c.JSON(http.StatusOK, themes)
|
2016-12-08 08:12:18 +00:00
|
|
|
}
|
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
func showTheme(c *gin.Context) {
|
2022-05-24 19:25:51 +00:00
|
|
|
theme := c.MustGet("theme").(*fic.Theme)
|
|
|
|
|
|
|
|
var forgelink string
|
|
|
|
if fli, ok := sync.GlobalImporter.(sync.ForgeLinkedImporter); ok {
|
|
|
|
if u, _ := fli.GetThemeLink(theme); u != nil {
|
|
|
|
forgelink = u.String()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, Theme{theme, forgelink})
|
2016-12-08 08:12:18 +00:00
|
|
|
}
|
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
func createTheme(c *gin.Context) {
|
2018-12-02 10:43:24 +00:00
|
|
|
var ut fic.Theme
|
2022-05-16 09:38:46 +00:00
|
|
|
err := c.ShouldBindJSON(&ut)
|
|
|
|
if err != nil {
|
|
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
|
|
|
return
|
2016-12-08 08:12:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(ut.Name) == 0 {
|
2022-05-16 09:38:46 +00:00
|
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Theme's name not filled"})
|
|
|
|
return
|
2016-12-08 08:12:18 +00:00
|
|
|
}
|
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
th, err := fic.CreateTheme(&ut)
|
|
|
|
if err != nil {
|
|
|
|
log.Println("Unable to createTheme:", err.Error())
|
|
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during theme creation."})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, th)
|
2016-12-08 08:12:18 +00:00
|
|
|
}
|
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
func updateTheme(c *gin.Context) {
|
|
|
|
theme := c.MustGet("theme").(*fic.Theme)
|
|
|
|
|
2016-12-08 08:12:18 +00:00
|
|
|
var ut fic.Theme
|
2022-05-16 09:38:46 +00:00
|
|
|
err := c.ShouldBindJSON(&ut)
|
|
|
|
if err != nil {
|
|
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
|
|
|
return
|
2016-12-08 08:12:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ut.Id = theme.Id
|
|
|
|
|
|
|
|
if len(ut.Name) == 0 {
|
2022-05-16 09:38:46 +00:00
|
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Theme's name not filled"})
|
|
|
|
return
|
2016-12-08 08:12:18 +00:00
|
|
|
}
|
|
|
|
|
2016-12-26 00:14:46 +00:00
|
|
|
if _, err := ut.Update(); err != nil {
|
2022-05-16 09:38:46 +00:00
|
|
|
log.Println("Unable to updateTheme:", err.Error())
|
|
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "An error occurs during theme update."})
|
|
|
|
return
|
2016-12-08 08:12:18 +00:00
|
|
|
}
|
2022-05-16 09:38:46 +00:00
|
|
|
|
2023-03-20 14:20:20 +00:00
|
|
|
if theme.Locked != ut.Locked {
|
|
|
|
exercices, err := theme.GetExercices()
|
|
|
|
if err != nil {
|
|
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
for _, exercice := range exercices {
|
|
|
|
if exercice.Disabled != ut.Locked {
|
|
|
|
exercice.Disabled = ut.Locked
|
|
|
|
_, err = exercice.Update()
|
|
|
|
if err != nil {
|
|
|
|
log.Println("Unable to enable/disable exercice: ", exercice.Id, err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
c.JSON(http.StatusOK, ut)
|
2016-12-08 08:12:18 +00:00
|
|
|
}
|
|
|
|
|
2022-05-16 09:38:46 +00:00
|
|
|
func deleteTheme(c *gin.Context) {
|
|
|
|
theme := c.MustGet("theme").(*fic.Theme)
|
|
|
|
|
|
|
|
_, err := theme.Delete()
|
|
|
|
if err != nil {
|
|
|
|
log.Println("Unable to deleteTheme:", err.Error())
|
|
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "An error occurs during theme deletion."})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, true)
|
2016-12-08 08:12:18 +00:00
|
|
|
}
|
2020-01-29 14:57:34 +00:00
|
|
|
|
2024-03-12 09:38:41 +00:00
|
|
|
func getThemedExercicesStats(c *gin.Context) {
|
|
|
|
theme := c.MustGet("theme").(*fic.Theme)
|
|
|
|
|
|
|
|
exercices, err := theme.GetExercices()
|
|
|
|
if err != nil {
|
|
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("Unable to fetch exercices: %s", err.Error())})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ret := []exerciceStats{}
|
|
|
|
for _, e := range exercices {
|
|
|
|
ret = append(ret, exerciceStats{
|
|
|
|
IdExercice: e.Id,
|
|
|
|
TeamTries: e.TriedTeamCount(),
|
|
|
|
TotalTries: e.TriedCount(),
|
|
|
|
SolvedCount: e.SolvedCount(),
|
|
|
|
FlagSolved: e.FlagSolved(),
|
|
|
|
MCQSolved: e.MCQSolved(),
|
|
|
|
})
|
2020-01-29 14:57:34 +00:00
|
|
|
}
|
2024-03-12 09:38:41 +00:00
|
|
|
c.JSON(http.StatusOK, ret)
|
2020-01-29 14:57:34 +00:00
|
|
|
}
|