// Package handler provides the HTTP handler for the marka server package handler import ( "errors" "fmt" "io" "net/http" "strings" "git.max-richter.dev/max/marka/renderer" "git.max-richter.dev/max/marka/server/internal/adapters" ) type Handler struct { adapter adapters.FileAdapter apiKey string } func (h *Handler) get(w http.ResponseWriter, target string) { entry, err := h.adapter.Read(target) if err != nil { if errors.Is(err, adapters.ErrNotFound) { writeError(w, http.StatusNotFound, err) return } writeError(w, http.StatusInternalServerError, err) return } if entry.Type == "file" { // For non-markdown files, content is []byte, write it directly if contentBytes, ok := entry.Content.([]byte); ok { w.Header().Set("Content-Type", entry.MIME) w.Write(contentBytes) return } // For markdown files, content is parsed, return the whole Entry as JSON writeJSON(w, http.StatusOK, entry) return } // For directories, return the whole Entry as JSON if entry.Type == "dir" { writeJSON(w, http.StatusOK, entry) return } writeError(w, http.StatusInternalServerError, errors.New("unknown entry type")) } func (h *Handler) post(w http.ResponseWriter, r *http.Request, target string) { if h.apiKey != "" { if r.Header.Get("Authentication") != h.apiKey { writeError(w, http.StatusUnauthorized, errors.New("invalid api key")) return } } else { writeError(w, http.StatusUnauthorized, errors.New("invalid api key")) return } body, err := io.ReadAll(r.Body) if err != nil { writeError(w, http.StatusBadRequest, err) return } defer r.Body.Close() contentType := r.Header.Get("Content-Type") isJSON := strings.HasPrefix(contentType, "application/json") var contentToWrite []byte if strings.HasSuffix(target, ".md") && isJSON { renderedContent, err := renderer.RenderFile(body) if err != nil { writeError(w, http.StatusInternalServerError, fmt.Errorf("failed to render file: %w", err)) return } contentToWrite = renderedContent } else { contentToWrite = body } if err := h.adapter.Write(target, contentToWrite); err != nil { writeError(w, http.StatusInternalServerError, fmt.Errorf("failed to write file: %w", err)) return } w.WriteHeader(http.StatusNoContent) } func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { reqPath := r.URL.Path if reqPath == "" { reqPath = "/" } target := cleanURLLike(reqPath) fmt.Printf("[serve] %s %s\n", r.Method, target) switch r.Method { case http.MethodGet: h.get(w, target) case http.MethodPost: h.post(w, r, target) default: writeError(w, http.StatusMethodNotAllowed, errors.New("method not allowed")) } } func NewHandler(adapter adapters.FileAdapter, apiKey string) http.Handler { return &Handler{ adapter: adapter, apiKey: apiKey, } }