Files
chart/viewer/handler.go
2026-03-15 21:41:38 +03:00

141 lines
3.3 KiB
Go

package viewer
import (
"errors"
"fmt"
"io"
"mime"
"net/http"
"strings"
"reanimator/chart/web"
)
type HandlerOptions struct {
Title string
Standalone bool
}
func NewHandler(opts HandlerOptions) http.Handler {
opts.Standalone = false
return newHandler(opts)
}
func NewStandaloneHandler(opts HandlerOptions) http.Handler {
opts.Standalone = true
return newHandler(opts)
}
func newHandler(opts HandlerOptions) http.Handler {
title := strings.TrimSpace(opts.Title)
if title == "" {
title = "Reanimator Chart"
}
mux := http.NewServeMux()
mux.Handle("GET /static/", http.StripPrefix("/static/", web.Static()))
mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("ok"))
})
mux.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
var (
html []byte
err error
)
if opts.Standalone {
html, err = web.RenderUpload(pageData{Title: title})
} else {
html, err = RenderHTML(nil, title)
}
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
_, _ = w.Write(html)
})
mux.HandleFunc("POST /render", func(w http.ResponseWriter, r *http.Request) {
payload, err := readSnapshotPayload(r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
page, err := buildPageData([]byte(payload), title)
if err != nil {
if opts.Standalone {
html, renderErr := web.RenderUpload(pageData{
Title: title,
Error: err.Error(),
})
if renderErr != nil {
http.Error(w, renderErr.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
_, _ = w.Write(html)
return
}
page = pageData{Title: title, Error: err.Error()}
}
html, renderErr := web.Render(page)
if renderErr != nil {
http.Error(w, renderErr.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
_, _ = w.Write(html)
})
return mux
}
func readSnapshotPayload(r *http.Request) (string, error) {
mediaType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
if err != nil {
return "", fmt.Errorf("parse content type: %w", err)
}
switch mediaType {
case "application/json":
body, err := io.ReadAll(r.Body)
if err != nil {
return "", fmt.Errorf("read request body: %w", err)
}
return string(body), nil
case "multipart/form-data":
if err := r.ParseMultipartForm(32 << 20); err != nil {
return "", fmt.Errorf("parse multipart form: %w", err)
}
payload, err := readSnapshotFile(r, "snapshot_file")
if err == nil && strings.TrimSpace(payload) != "" {
return payload, nil
}
if err != nil && !errors.Is(err, http.ErrMissingFile) {
return "", err
}
return r.FormValue("snapshot"), nil
default:
if err := r.ParseForm(); err != nil {
return "", fmt.Errorf("parse form: %w", err)
}
return r.FormValue("snapshot"), nil
}
}
func readSnapshotFile(r *http.Request, field string) (string, error) {
file, _, err := r.FormFile(field)
if err != nil {
return "", fmt.Errorf("read uploaded file: %w", err)
}
defer file.Close()
body, err := io.ReadAll(file)
if err != nil {
return "", fmt.Errorf("read uploaded file contents: %w", err)
}
return string(body), nil
}