2026-05-26 17:11:09 +02:00

121 lines
3.4 KiB
Go

package api
import (
"errors"
"fmt"
"net/http"
"printer.backend/internal/model"
"printer.backend/internal/platepdf"
"printer.backend/internal/store"
)
func registerPDFRoutes(mux *http.ServeMux, configs *store.ConfigurationStore, plates *store.PlateStore, items *store.ItemStore) {
mux.HandleFunc("GET /layout/pdf", layoutPDF(plates, items))
mux.HandleFunc("GET /configurations/{id}/pdf", configurationPDF(configs, plates, items))
}
func layoutPDF(plates *store.PlateStore, items *store.ItemStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
plateID := r.URL.Query().Get("plate_id")
itemID := r.URL.Query().Get("item_id")
if plateID == "" || itemID == "" {
writeError(w, http.StatusBadRequest, errors.New("plate_id and item_id query params are required"))
return
}
spacing, err := parseSpacing(r.URL.Query().Get("spacing_mm"))
if err != nil {
writeError(w, http.StatusBadRequest, err)
return
}
plate, item, preview, svgPath, err := resolveLayoutPDF(plates, items, plateID, itemID, spacing)
if err != nil {
writeError(w, http.StatusBadRequest, err)
return
}
pdf, err := platepdf.Generate(plate, item.Spec, svgPath, preview)
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
servePDF(w, pdf, "layout-preview.pdf")
}
}
func configurationPDF(configs *store.ConfigurationStore, plates *store.PlateStore, items *store.ItemStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
c, err := configs.Get(id)
if err != nil {
writeError(w, http.StatusNotFound, err)
return
}
plate, item, preview, svgPath, err := resolveLayoutPDF(plates, items, c.PlateID, c.ItemID, c.SpacingMM)
if err != nil {
writeError(w, http.StatusBadRequest, err)
return
}
pdf, err := platepdf.Generate(plate, item.Spec, svgPath, preview)
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
name := c.Name
if name == "" {
name = "configuration-" + id[:8]
}
servePDF(w, pdf, sanitizeFilename(name)+".pdf")
}
}
func resolveLayoutPDF(plates *store.PlateStore, items *store.ItemStore, plateID, itemID string, spacingMM float64) (
plate model.Plate, item model.Item, preview model.LayoutPreview, svgPath string, err error,
) {
plate, err = findPlate(plates, plateID)
if err != nil {
return plate, item, preview, "", err
}
item, err = items.Get(itemID)
if err != nil {
return plate, item, preview, "", err
}
svgPath, err = items.SVGPath(itemID)
if err != nil {
return plate, item, preview, "", err
}
preview, err = buildPreview(plates, items, plateID, itemID, spacingMM)
if err != nil {
return plate, item, preview, "", err
}
preview.PlateID = plateID
preview.ItemID = itemID
return plate, item, preview, svgPath, nil
}
func servePDF(w http.ResponseWriter, pdf []byte, filename string) {
w.Header().Set("Content-Type", "application/pdf")
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filename))
w.Header().Set("Cache-Control", "no-cache")
_, _ = w.Write(pdf)
}
func sanitizeFilename(name string) string {
var b []byte
for i := 0; i < len(name); i++ {
c := name[i]
if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_' {
b = append(b, c)
} else if c == ' ' {
b = append(b, '_')
}
}
if len(b) == 0 {
return "preview"
}
return string(b)
}