package api import ( "fmt" "log" "net/http" "path" "strconv" "srs.epita.fr/fic-server/admin/sync" "srs.epita.fr/fic-server/libfic" "srs.epita.fr/fic-server/settings" "github.com/gin-gonic/gin" ) 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) { if s, err := settings.ReadSettings(path.Join(settings.SettingsDir, settings.SettingsFile)); err != nil { log.Printf("Unable to ReadSettings: %s", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during settings reading."}) return } 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 { 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 } else { c.JSON(http.StatusOK, sf) } }) router.GET("/files-bindings", bindingFiles) apiThemesRoutes := router.Group("/themes/:thid") apiThemesRoutes.Use(ThemeHandler) apiThemesRoutes.GET("", showTheme) apiThemesRoutes.PUT("", updateTheme) apiThemesRoutes.DELETE("", deleteTheme) apiThemesRoutes.GET("/exercices_stats.json", getThemedExercicesStats) declareExercicesRoutes(apiThemesRoutes) // Remote router.GET("/remote/themes", sync.ApiListRemoteThemes) router.GET("/remote/themes/:thid", sync.ApiGetRemoteTheme) router.GET("/remote/themes/:thid/exercices", sync.ApiListRemoteExercices) } type Theme struct { *fic.Theme ForgeLink string `json:"forge_link,omitempty"` } 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 } theme, err := fic.GetTheme(thid) if err != nil { c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Theme not found"}) return } c.Set("theme", theme) c.Next() } func fixAllURLIds(c *gin.Context) { 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 } } } } } c.JSON(http.StatusOK, nbFix) } func bindingFiles(c *gin.Context) { files, err := fic.GetFiles() if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } ret := "" for _, file := range files { ret += fmt.Sprintf("%s;%s\n", file.GetOrigin(), file.Path) } c.String(http.StatusOK, ret) } 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 } c.JSON(http.StatusOK, themes) } 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 } c.JSON(http.StatusOK, themes) } func showTheme(c *gin.Context) { 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}) } func createTheme(c *gin.Context) { var ut fic.Theme err := c.ShouldBindJSON(&ut) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) return } if len(ut.Name) == 0 { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Theme's name not filled"}) return } 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) } func updateTheme(c *gin.Context) { theme := c.MustGet("theme").(*fic.Theme) var ut fic.Theme err := c.ShouldBindJSON(&ut) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) return } ut.Id = theme.Id if len(ut.Name) == 0 { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Theme's name not filled"}) return } if _, err := ut.Update(); err != nil { log.Println("Unable to updateTheme:", err.Error()) c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "An error occurs during theme update."}) return } 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()) } } } } c.JSON(http.StatusOK, ut) } 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) } 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(), }) } c.JSON(http.StatusOK, ret) }