214 lines
5.5 KiB
Go
214 lines
5.5 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"mime"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"printer.backend/internal/model"
|
|
"printer.backend/internal/store"
|
|
)
|
|
|
|
func registerOrderRoutes(mux *http.ServeMux, orders *store.OrderStore) {
|
|
mux.HandleFunc("GET /orders", listOrders(orders))
|
|
mux.HandleFunc("POST /orders", saveOrder(orders))
|
|
mux.HandleFunc("GET /orders/{id}", getOrder(orders))
|
|
mux.HandleFunc("PUT /orders/{id}", updateOrder(orders))
|
|
mux.HandleFunc("DELETE /orders/{id}", deleteOrder(orders))
|
|
mux.HandleFunc("POST /orders/{id}/images", addOrderImages(orders))
|
|
mux.HandleFunc("DELETE /orders/{id}/images/{imageId}", deleteOrderImage(orders))
|
|
mux.HandleFunc("GET /orders/{id}/images/{imageId}", serveOrderImage(orders))
|
|
}
|
|
|
|
func listOrders(s *store.OrderStore) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, _ *http.Request) {
|
|
list, err := s.List()
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
if list == nil {
|
|
list = []model.Order{}
|
|
}
|
|
for i := range list {
|
|
if list[i].Images == nil {
|
|
list[i].Images = []model.OrderImage{}
|
|
}
|
|
}
|
|
writeJSON(w, http.StatusOK, list)
|
|
}
|
|
}
|
|
|
|
type saveOrderRequest struct {
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
func saveOrder(s *store.OrderStore) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
var req saveOrderRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeError(w, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
saved, err := s.Save(model.Order{Name: req.Name})
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusCreated, saved)
|
|
}
|
|
}
|
|
|
|
func getOrder(s *store.OrderStore) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
id := r.PathValue("id")
|
|
o, err := s.Get(id)
|
|
if err != nil {
|
|
writeError(w, http.StatusNotFound, err)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, o)
|
|
}
|
|
}
|
|
|
|
func updateOrder(s *store.OrderStore) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
id := r.PathValue("id")
|
|
var req saveOrderRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeError(w, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
saved, err := s.Update(id, model.Order{Name: req.Name})
|
|
if err != nil {
|
|
writeError(w, http.StatusNotFound, err)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusOK, saved)
|
|
}
|
|
}
|
|
|
|
func deleteOrder(s *store.OrderStore) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
id := r.PathValue("id")
|
|
if err := s.Delete(id); err != nil {
|
|
writeError(w, http.StatusNotFound, err)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|
|
}
|
|
|
|
func addOrderImages(s *store.OrderStore) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
orderID := r.PathValue("id")
|
|
if _, err := s.Get(orderID); err != nil {
|
|
writeError(w, http.StatusNotFound, err)
|
|
return
|
|
}
|
|
|
|
if err := r.ParseMultipartForm(32 << 20); err != nil {
|
|
writeError(w, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
var files []*multipart.FileHeader
|
|
if r.MultipartForm != nil && len(r.MultipartForm.File["images"]) > 0 {
|
|
files = r.MultipartForm.File["images"]
|
|
} else if _, fh, err := r.FormFile("image"); err == nil {
|
|
files = []*multipart.FileHeader{fh}
|
|
} else {
|
|
writeError(w, http.StatusBadRequest, errors.New("image or images field required"))
|
|
return
|
|
}
|
|
|
|
added := make([]model.OrderImage, 0, len(files))
|
|
for _, fh := range files {
|
|
data, contentType, err := readUpload(fh)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
img, err := s.AddImage(orderID, fh.Filename, data, contentType)
|
|
if err != nil {
|
|
writeError(w, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
added = append(added, img)
|
|
}
|
|
|
|
order, err := s.Get(orderID)
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusCreated, map[string]any{
|
|
"added": added,
|
|
"order": order,
|
|
})
|
|
}
|
|
}
|
|
|
|
func readUpload(fh *multipart.FileHeader) ([]byte, string, error) {
|
|
rc, err := fh.Open()
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
defer rc.Close()
|
|
|
|
const maxImageSize = 32 << 20
|
|
data, err := io.ReadAll(io.LimitReader(rc, maxImageSize+1))
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
if len(data) > maxImageSize {
|
|
return nil, "", errors.New("image too large")
|
|
}
|
|
contentType := http.DetectContentType(data)
|
|
return data, contentType, nil
|
|
}
|
|
|
|
func deleteOrderImage(s *store.OrderStore) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
orderID := r.PathValue("id")
|
|
imageID := r.PathValue("imageId")
|
|
if err := s.DeleteImage(orderID, imageID); err != nil {
|
|
writeError(w, http.StatusNotFound, err)
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|
|
}
|
|
|
|
func serveOrderImage(s *store.OrderStore) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
orderID := r.PathValue("id")
|
|
imageID := r.PathValue("imageId")
|
|
path, img, err := s.ImagePath(orderID, imageID)
|
|
if err != nil {
|
|
writeError(w, http.StatusNotFound, err)
|
|
return
|
|
}
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
writeError(w, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
contentType := img.ContentType
|
|
if contentType == "" {
|
|
contentType = mime.TypeByExtension(filepath.Ext(path))
|
|
}
|
|
if contentType == "" || contentType == "application/octet-stream" {
|
|
contentType = http.DetectContentType(data)
|
|
}
|
|
w.Header().Set("Content-Type", contentType)
|
|
w.Header().Set("Cache-Control", "no-cache")
|
|
_, _ = w.Write(data)
|
|
}
|
|
}
|