2026-05-26 18:27:37 +02:00

256 lines
6.9 KiB
Go

package api
import (
"encoding/json"
"errors"
"net/http"
"time"
"printer.backend/internal/model"
"printer.backend/internal/printjob"
"printer.backend/internal/store"
)
func registerPrintJobRoutes(
mux *http.ServeMux,
jobs *store.PrintJobStore,
configs *store.ConfigurationStore,
plates *store.PlateStore,
items *store.ItemStore,
orders *store.OrderStore,
) {
mux.HandleFunc("GET /print-jobs", listPrintJobs(jobs, configs, plates, items, orders))
mux.HandleFunc("POST /print-jobs", savePrintJob(jobs, configs, orders))
mux.HandleFunc("GET /print-jobs/{id}", getPrintJob(jobs, configs, plates, items, orders))
mux.HandleFunc("PUT /print-jobs/{id}", updatePrintJob(jobs, configs, orders))
mux.HandleFunc("DELETE /print-jobs/{id}", deletePrintJob(jobs))
mux.HandleFunc("GET /print-jobs/{id}/summary", printJobSummary(jobs, configs, plates, items, orders))
mux.HandleFunc("GET /print-jobs/{id}/pdf", printJobPDF(jobs, configs, plates, items, orders))
}
type printJobResponse struct {
model.PrintJob
Summary *model.PrintJobSummary `json:"summary,omitempty"`
}
type savePrintJobRequest struct {
Name string `json:"name"`
ConfigurationID string `json:"configuration_id"`
OrderIDs []string `json:"order_ids"`
}
func listPrintJobs(
jobs *store.PrintJobStore,
configs *store.ConfigurationStore,
plates *store.PlateStore,
items *store.ItemStore,
orders *store.OrderStore,
) http.HandlerFunc {
return func(w http.ResponseWriter, _ *http.Request) {
list, err := jobs.List()
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
if list == nil {
list = []model.PrintJob{}
}
out := make([]printJobResponse, 0, len(list))
for _, j := range list {
out = append(out, printJobResponseFor(j, configs, plates, items, orders))
}
writeJSON(w, http.StatusOK, out)
}
}
func savePrintJob(jobs *store.PrintJobStore, configs *store.ConfigurationStore, orders *store.OrderStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req savePrintJobRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, err)
return
}
if err := validatePrintJobRequest(req, configs, orders); err != nil {
writeError(w, http.StatusBadRequest, err)
return
}
j := model.PrintJob{
Name: req.Name,
ConfigurationID: req.ConfigurationID,
OrderIDs: req.OrderIDs,
CreatedAt: time.Now().UTC(),
}
saved, err := jobs.Save(j)
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
writeJSON(w, http.StatusCreated, saved)
}
}
func getPrintJob(
jobs *store.PrintJobStore,
configs *store.ConfigurationStore,
plates *store.PlateStore,
items *store.ItemStore,
orders *store.OrderStore,
) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
j, err := jobs.Get(r.PathValue("id"))
if err != nil {
writeError(w, http.StatusNotFound, err)
return
}
writeJSON(w, http.StatusOK, printJobResponseFor(j, configs, plates, items, orders))
}
}
func updatePrintJob(jobs *store.PrintJobStore, configs *store.ConfigurationStore, orders *store.OrderStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
var req savePrintJobRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, err)
return
}
if err := validatePrintJobRequest(req, configs, orders); err != nil {
writeError(w, http.StatusBadRequest, err)
return
}
j := model.PrintJob{
Name: req.Name,
ConfigurationID: req.ConfigurationID,
OrderIDs: req.OrderIDs,
}
saved, err := jobs.Update(id, j)
if err != nil {
writeError(w, http.StatusNotFound, err)
return
}
writeJSON(w, http.StatusOK, saved)
}
}
func deletePrintJob(jobs *store.PrintJobStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if err := jobs.Delete(r.PathValue("id")); err != nil {
writeError(w, http.StatusNotFound, err)
return
}
w.WriteHeader(http.StatusNoContent)
}
}
func printJobSummary(
jobs *store.PrintJobStore,
configs *store.ConfigurationStore,
plates *store.PlateStore,
items *store.ItemStore,
orders *store.OrderStore,
) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
j, err := jobs.Get(r.PathValue("id"))
if err != nil {
writeError(w, http.StatusNotFound, err)
return
}
summary, err := buildPrintJobSummary(j, configs, plates, items, orders)
if err != nil {
writeError(w, http.StatusBadRequest, err)
return
}
writeJSON(w, http.StatusOK, summary)
}
}
func printJobPDF(
jobs *store.PrintJobStore,
configs *store.ConfigurationStore,
plates *store.PlateStore,
items *store.ItemStore,
orders *store.OrderStore,
) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
j, err := jobs.Get(r.PathValue("id"))
if err != nil {
writeError(w, http.StatusNotFound, err)
return
}
pdf, summary, err := printjob.RenderPDF(j, configs, plates, items, orders)
if err != nil {
writeError(w, http.StatusBadRequest, err)
return
}
if summary.ImageOverflow {
w.Header().Set("X-Print-Job-Warning", summary.Warning)
}
name := j.Name
if name == "" {
name = "print-job-" + j.ID[:8]
}
servePDF(w, pdf, sanitizeFilename(name)+".pdf")
}
}
func validatePrintJobRequest(req savePrintJobRequest, configs *store.ConfigurationStore, orders *store.OrderStore) error {
if req.ConfigurationID == "" {
return errors.New("configuration_id is required")
}
if _, err := configs.Get(req.ConfigurationID); err != nil {
return errors.New("configuration not found: " + req.ConfigurationID)
}
if len(req.OrderIDs) == 0 {
return errors.New("at least one order_id is required")
}
for _, id := range req.OrderIDs {
if _, err := orders.Get(id); err != nil {
return errors.New("order not found: " + id)
}
}
return nil
}
func printJobResponseFor(
j model.PrintJob,
configs *store.ConfigurationStore,
plates *store.PlateStore,
items *store.ItemStore,
orders *store.OrderStore,
) printJobResponse {
resp := printJobResponse{PrintJob: j}
if summary, err := buildPrintJobSummary(j, configs, plates, items, orders); err == nil {
resp.Summary = &summary
}
return resp
}
func buildPrintJobSummary(
j model.PrintJob,
configs *store.ConfigurationStore,
plates *store.PlateStore,
items *store.ItemStore,
orders *store.OrderStore,
) (model.PrintJobSummary, error) {
cfg, err := configs.Get(j.ConfigurationID)
if err != nil {
return model.PrintJobSummary{}, err
}
preview, err := buildPreview(plates, items, cfg.PlateID, cfg.ItemID, cfg.SpacingMM)
if err != nil {
return model.PrintJobSummary{}, err
}
orderList := make([]model.Order, 0, len(j.OrderIDs))
for _, id := range j.OrderIDs {
o, err := orders.Get(id)
if err != nil {
return model.PrintJobSummary{}, err
}
orderList = append(orderList, o)
}
return printjob.Summary(orderList, preview), nil
}