117 lines
2.7 KiB
Go
117 lines
2.7 KiB
Go
// 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)
|
|
|
|
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,
|
|
}
|
|
}
|