package handlers import ( "encoding/json" "net/http" "strings" "github.com/sirupsen/logrus" "markdown-editor-backend/storage" ) // Handler contains HTTP handlers for the API type Handler struct { storage *storage.Storage log *logrus.Logger } // New creates a new Handler instance func New(storage *storage.Storage, log *logrus.Logger) *Handler { return &Handler{ storage: storage, log: log, } } // FileRequest represents a request body for file operations type FileRequest struct { Name string `json:"name"` Content string `json:"content"` } // FileResponse represents a file response type FileResponse struct { Name string `json:"name"` Content string `json:"content,omitempty"` } // ErrorResponse represents an error response type ErrorResponse struct { Error string `json:"error"` } // FilesHandler handles list and create operations func (h *Handler) FilesHandler(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: h.listFiles(w, r) case http.MethodPost: h.createFile(w, r) default: h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") } } // FileHandler handles get, update, and delete operations for a specific file func (h *Handler) FileHandler(w http.ResponseWriter, r *http.Request) { // Extract filename from URL path path := strings.TrimPrefix(r.URL.Path, "/api/files/") if path == "" { h.sendError(w, http.StatusBadRequest, "filename required") return } name := strings.TrimSuffix(path, ".md") switch r.Method { case http.MethodGet: h.getFile(w, r, name) case http.MethodPut: h.updateFile(w, r, name) case http.MethodDelete: h.deleteFile(w, r, name) default: h.sendError(w, http.StatusMethodNotAllowed, "method not allowed") } } func (h *Handler) listFiles(w http.ResponseWriter, r *http.Request) { h.log.Debug("Handling list files request") files, err := h.storage.List() if err != nil { h.log.WithError(err).Error("Failed to list files") h.sendError(w, http.StatusInternalServerError, "failed to list files") return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(files) } func (h *Handler) createFile(w http.ResponseWriter, r *http.Request) { h.log.Debug("Handling create file request") var req FileRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { h.log.WithError(err).Warn("Invalid request body") h.sendError(w, http.StatusBadRequest, "invalid request body") return } if req.Name == "" { h.sendError(w, http.StatusBadRequest, "name is required") return } // Normalize filename by stripping .md extension if present name := strings.TrimSuffix(req.Name, ".md") if err := h.storage.Save(name, req.Content); err != nil { h.log.WithError(err).Error("Failed to create file") h.sendError(w, http.StatusInternalServerError, "failed to create file") return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(FileResponse{Name: name, Content: req.Content}) } func (h *Handler) getFile(w http.ResponseWriter, r *http.Request, name string) { h.log.WithField("name", name).Debug("Handling get file request") content, err := h.storage.Get(name) if err != nil { if err.Error() == "file not found" { h.sendError(w, http.StatusNotFound, "file not found") return } h.log.WithError(err).Error("Failed to get file") h.sendError(w, http.StatusInternalServerError, "failed to get file") return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(FileResponse{Name: name, Content: content}) } func (h *Handler) updateFile(w http.ResponseWriter, r *http.Request, name string) { h.log.WithField("name", name).Debug("Handling update file request") var req FileRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { h.log.WithError(err).Warn("Invalid request body") h.sendError(w, http.StatusBadRequest, "invalid request body") return } if err := h.storage.Save(name, req.Content); err != nil { h.log.WithError(err).Error("Failed to update file") h.sendError(w, http.StatusInternalServerError, "failed to update file") return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(FileResponse{Name: name, Content: req.Content}) } func (h *Handler) deleteFile(w http.ResponseWriter, r *http.Request, name string) { h.log.WithField("name", name).Debug("Handling delete file request") if err := h.storage.Delete(name); err != nil { if err.Error() == "file not found" { h.sendError(w, http.StatusNotFound, "file not found") return } h.log.WithError(err).Error("Failed to delete file") h.sendError(w, http.StatusInternalServerError, "failed to delete file") return } w.WriteHeader(http.StatusNoContent) } func (h *Handler) sendError(w http.ResponseWriter, status int, message string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) json.NewEncoder(w).Encode(ErrorResponse{Error: message}) }