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 }