admin: Use gin-gonic as router

This commit is contained in:
nemunaire 2022-05-16 11:38:46 +02:00
parent 83468ad723
commit 8b3fbdb64a
32 changed files with 2785 additions and 1635 deletions

View file

@ -2,43 +2,79 @@ package api
import (
"encoding/hex"
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"srs.epita.fr/fic-server/admin/sync"
"srs.epita.fr/fic-server/libfic"
"github.com/julienschmidt/httprouter"
"github.com/gin-gonic/gin"
)
func init() {
router.GET("/api/files/", apiHandler(listFiles))
router.DELETE("/api/files/", apiHandler(clearFiles))
router.GET("/api/files/:fileid", apiHandler(fileHandler(showFile)))
router.PUT("/api/files/:fileid", apiHandler(fileHandler(updateFile)))
router.DELETE("/api/files/:fileid", apiHandler(fileHandler(deleteFile)))
router.DELETE("/api/files/:fileid/dependancies/:depid", apiHandler(fileDependancyHandler(deleteFileDep)))
router.GET("/api/exercices/:eid/files", apiHandler(exerciceHandler(listExerciceFiles)))
router.POST("/api/exercices/:eid/files", apiHandler(exerciceHandler(createExerciceFile)))
router.GET("/api/exercices/:eid/files/:fid", apiHandler(exerciceFileHandler(showFile)))
router.PUT("/api/exercices/:eid/files/:fid", apiHandler(exerciceFileHandler(updateFile)))
router.DELETE("/api/exercices/:eid/files/:fid", apiHandler(exerciceFileHandler(deleteFile)))
func declareFilesGlobalRoutes(router *gin.RouterGroup) {
router.DELETE("/files/", clearFiles)
// Remote
router.GET("/api/remote/themes/:thid/exercices/:exid/files", apiHandler(sync.ApiGetRemoteExerciceFiles))
router.GET("/remote/themes/:thid/exercices/:exid/files", sync.ApiGetRemoteExerciceFiles)
}
func declareFilesRoutes(router *gin.RouterGroup) {
router.GET("/files", listFiles)
router.POST("/files", createExerciceFile)
apiFilesRoutes := router.Group("/files/:fileid")
apiFilesRoutes.Use(FileHandler)
apiFilesRoutes.GET("", showFile)
apiFilesRoutes.PUT("", updateFile)
apiFilesRoutes.DELETE("", deleteFile)
apiFileDepsRoutes := apiFilesRoutes.Group("/dependancies/:depid")
apiFileDepsRoutes.Use(FileDepHandler)
apiFileDepsRoutes.DELETE("", deleteFileDep)
// Check
router.POST("/api/files/:fileid/check", apiHandler(fileHandler(checkFile)))
apiFilesRoutes.POST("/check", checkFile)
}
// Synchronize
router.POST("/api/sync/exercices/:eid/files", apiHandler(exerciceHandler(
func(exercice *fic.Exercice, _ []byte) (interface{}, error) {
return sync.SyncExerciceFiles(sync.GlobalImporter, exercice), nil
})))
func FileHandler(c *gin.Context) {
fileid, err := strconv.ParseInt(string(c.Params.ByName("fileid")), 10, 64)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid file identifier"})
return
}
var file *fic.EFile
if exercice, exists := c.Get("exercice"); exists {
file, err = exercice.(*fic.Exercice).GetFile(fileid)
if err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "File not found"})
return
}
} else {
file, err = fic.GetFile(fileid)
if err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "File not found"})
return
}
}
c.Set("file", file)
c.Next()
}
func FileDepHandler(c *gin.Context) {
depid, err := strconv.ParseInt(string(c.Params.ByName("depid")), 10, 64)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid dependency identifier"})
return
}
c.Set("file-depid", depid)
c.Next()
}
type APIFile struct {
@ -87,20 +123,35 @@ func genFileList(in []*fic.EFile, e error) (out []APIFile, err error) {
return
}
func listFiles(_ httprouter.Params, body []byte) (interface{}, error) {
return genFileList(fic.GetFiles())
func listFiles(c *gin.Context) {
var files []APIFile
var err error
if exercice, exists := c.Get("exercice"); exists {
files, err = genFileList(exercice.(*fic.Exercice).GetFiles())
} else {
files, err = genFileList(fic.GetFiles())
}
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
return
}
c.JSON(http.StatusOK, files)
}
func listExerciceFiles(exercice *fic.Exercice, body []byte) (interface{}, error) {
return genFileList(exercice.GetFiles())
func clearFiles(c *gin.Context) {
_, err := fic.ClearFiles()
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
return
}
c.JSON(http.StatusOK, true)
}
func clearFiles(_ httprouter.Params, _ []byte) (interface{}, error) {
return fic.ClearFiles()
}
func showFile(file *fic.EFile, _ []byte) (interface{}, error) {
return file, nil
func showFile(c *gin.Context) {
c.JSON(http.StatusOK, c.MustGet("file").(*fic.EFile))
}
type uploadedFile struct {
@ -108,45 +159,92 @@ type uploadedFile struct {
Digest string
}
func createExerciceFile(exercice *fic.Exercice, body []byte) (interface{}, error) {
var uf uploadedFile
if err := json.Unmarshal(body, &uf); err != nil {
return nil, err
func createExerciceFile(c *gin.Context) {
exercice, exists := c.Get("exercice")
if !exists {
c.AbortWithStatusJSON(http.StatusMethodNotAllowed, gin.H{"errmsg": "File can only be added inside an exercice."})
return
}
return sync.ImportFile(sync.GlobalImporter, uf.URI,
var uf uploadedFile
err := c.ShouldBindJSON(&uf)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
return
}
ret, err := sync.ImportFile(sync.GlobalImporter, uf.URI,
func(filePath string, origin string) (interface{}, error) {
if digest, err := hex.DecodeString(uf.Digest); err != nil {
return nil, err
} else {
return exercice.ImportFile(filePath, origin, digest)
return exercice.(*fic.Exercice).ImportFile(filePath, origin, digest)
}
})
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
return
}
c.JSON(http.StatusOK, ret)
}
func updateFile(file *fic.EFile, body []byte) (interface{}, error) {
func updateFile(c *gin.Context) {
file := c.MustGet("file").(*fic.EFile)
var uf fic.EFile
if err := json.Unmarshal(body, &uf); err != nil {
return nil, err
err := c.ShouldBindJSON(&uf)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
return
}
uf.Id = file.Id
if _, err := uf.Update(); err != nil {
return nil, err
} else {
return uf, nil
log.Println("Unable to updateFile:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to update file."})
return
}
c.JSON(http.StatusOK, uf)
}
func deleteFile(file *fic.EFile, _ []byte) (interface{}, error) {
return file.Delete()
func deleteFile(c *gin.Context) {
file := c.MustGet("file").(*fic.EFile)
_, err := file.Delete()
if err != nil {
log.Println("Unable to updateFile:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to update file."})
return
}
c.JSON(http.StatusOK, true)
}
func deleteFileDep(file *fic.EFile, depid int, _ []byte) (interface{}, error) {
return true, file.DeleteDepend(&fic.FlagKey{Id: depid})
func deleteFileDep(c *gin.Context) {
file := c.MustGet("file").(*fic.EFile)
depid := c.MustGet("file-depid").(int64)
err := file.DeleteDepend(&fic.FlagKey{Id: int(depid)})
if err != nil {
log.Println("Unable to deleteFileDep:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to delete file dependency."})
return
}
c.JSON(http.StatusOK, true)
}
func checkFile(file *fic.EFile, _ []byte) (interface{}, error) {
return true, file.CheckFileOnDisk()
func checkFile(c *gin.Context) {
file := c.MustGet("file").(*fic.EFile)
err := file.CheckFileOnDisk()
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()})
return
}
c.JSON(http.StatusOK, true)
}