package api import ( "net/http" "github.com/sirupsen/logrus" "markdown-editor/internal/storage" "markdown-editor/internal/logger" "github.com/gin-gonic/gin" ) type API struct { storage *storage.Storage log *logrus.Logger } func NewAPI(storage *storage.Storage) *API { log := logger.GetLogger() return &API{ storage: storage, log: log, } } // FileInfo represents a file for API responses type FileInfo struct { Name string `json:"name"` Content string `json:"content,omitempty"` } // File represents a file for API requests type File struct { Name string `json:"name"` Content string `json:"content"` } // ErrorResponse represents an error response type ErrorResponse struct { Error string `json:"error"` } func (a *API) RegisterRoutes(router *gin.Engine) { // File endpoints router.GET("/api/files", a.listFiles) router.GET("/api/files/:name", a.getFile) router.POST("/api/files", a.createFile) router.PUT("/api/files/:name", a.updateFile) router.DELETE("/api/files/:name", a.deleteFile) } func (a *API) listFiles(c *gin.Context) { a.log.Info("Handling GET /api/files") files, err := a.storage.ListFiles() if err != nil { a.log.Errorf("Failed to list files: %v", err) c.JSON(http.StatusInternalServerError, ErrorResponse{Error: "Failed to list files"}) return } c.JSON(http.StatusOK, gin.H{ "files": files, }) } func (a *API) getFile(c *gin.Context) { name := c.Param("name") a.log.WithField("filename", name).Info("Handling GET /api/files/:name") file, err := a.storage.GetFile(name) if err != nil { a.log.Errorf("Failed to get file %s: %v", name, err) if err.Error() == "file not found: "+name { c.JSON(http.StatusNotFound, ErrorResponse{Error: "File not found"}) return } c.JSON(http.StatusBadRequest, ErrorResponse{Error: err.Error()}) return } c.JSON(http.StatusOK, file) } func (a *API) createFile(c *gin.Context) { var file File if err := c.ShouldBindJSON(&file); err != nil { a.log.Errorf("Invalid request body: %v", err) c.JSON(http.StatusBadRequest, ErrorResponse{Error: "Invalid request body"}) return } a.log.WithField("filename", file.Name).Info("Handling POST /api/files") if file.Name == "" { c.JSON(http.StatusBadRequest, ErrorResponse{Error: "File name is required"}) return } if err := a.storage.CreateFile(file.Name, file.Content); err != nil { a.log.Errorf("Failed to create file %s: %v", file.Name, err) if err.Error() == "file already exists: "+file.Name { c.JSON(http.StatusConflict, ErrorResponse{Error: "File already exists"}) return } c.JSON(http.StatusBadRequest, ErrorResponse{Error: err.Error()}) return } c.JSON(http.StatusCreated, file) } func (a *API) updateFile(c *gin.Context) { name := c.Param("name") var file File if err := c.ShouldBindJSON(&file); err != nil { a.log.Errorf("Invalid request body: %v", err) c.JSON(http.StatusBadRequest, ErrorResponse{Error: "Invalid request body"}) return } a.log.WithField("filename", name).Info("Handling PUT /api/files/:name") if file.Name != name { c.JSON(http.StatusBadRequest, ErrorResponse{Error: "File name in path and body must match"}) return } if err := a.storage.UpdateFile(name, file.Content); err != nil { a.log.Errorf("Failed to update file %s: %v", name, err) c.JSON(http.StatusBadRequest, ErrorResponse{Error: err.Error()}) return } c.JSON(http.StatusOK, file) } func (a *API) deleteFile(c *gin.Context) { name := c.Param("name") a.log.WithField("filename", name).Info("Handling DELETE /api/files/:name") if err := a.storage.DeleteFile(name); err != nil { a.log.Errorf("Failed to delete file %s: %v", name, err) c.JSON(http.StatusBadRequest, ErrorResponse{Error: err.Error()}) return } c.JSON(http.StatusNoContent, nil) }