Added Model Orders and Picture Upload
This commit is contained in:
@@ -16,6 +16,7 @@ func NewHandler() http.Handler {
|
||||
plates := store.NewPlateStore("")
|
||||
items := store.NewItemStore("")
|
||||
configs := store.NewConfigurationStore("")
|
||||
orders := store.NewOrderStore("")
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("GET /health", health)
|
||||
@@ -30,6 +31,7 @@ func NewHandler() http.Handler {
|
||||
mux.HandleFunc("DELETE /items/{id}", deleteItem(items))
|
||||
mux.HandleFunc("GET /items/{id}/svg", serveItemSVG(items))
|
||||
registerConfigurationRoutes(mux, configs, plates, items)
|
||||
registerOrderRoutes(mux, orders)
|
||||
return withCORS(mux)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user