apps/cellar/forms.go 3.5 K raw
1
package main
2
3
import (
4
	"errors"
5
	"io"
6
	"mime/multipart"
7
	"net/http"
8
	"strconv"
9
	"strings"
10
)
11
12
const maxUploadBytes = 10 * 1024 * 1024
13
14
type wineFormData struct {
15
	Name       string
16
	Origin     string
17
	Grape      string
18
	Notes      string
19
	Background string
20
	Image      []byte
21
	ImageMime  string
22
23
	Sweetness      int
24
	Acidity        int
25
	Tannin         int
26
	Alcohol        int
27
	Body           int
28
	Clarity        int
29
	ColorIntensity int
30
	AromaIntensity int
31
	NoseComplexity int
32
}
33
34
func clamp1to5(v int) int {
35
	if v < 1 {
36
		return 1
37
	}
38
	if v > 5 {
39
		return 5
40
	}
41
	return v
42
}
43
44
func parseWineMultipart(r *http.Request) (*wineFormData, error) {
45
	r.Body = http.MaxBytesReader(nil, r.Body, maxUploadBytes)
46
	if err := r.ParseMultipartForm(maxUploadBytes); err != nil {
47
		return nil, err
48
	}
49
	data := &wineFormData{
50
		Sweetness: 3, Acidity: 3, Tannin: 3, Alcohol: 3, Body: 3,
51
		Clarity: 3, ColorIntensity: 3, AromaIntensity: 3, NoseComplexity: 3,
52
	}
53
	data.Name = strings.TrimSpace(r.FormValue("name"))
54
	data.Origin = strings.TrimSpace(r.FormValue("origin"))
55
	data.Grape = strings.TrimSpace(r.FormValue("grape"))
56
	data.Notes = strings.TrimSpace(r.FormValue("notes"))
57
	data.Background = strings.TrimSpace(r.FormValue("background"))
58
59
	scoreFields := map[string]*int{
60
		"sweetness": &data.Sweetness, "acidity": &data.Acidity, "tannin": &data.Tannin,
61
		"alcohol": &data.Alcohol, "body": &data.Body,
62
		"clarity": &data.Clarity, "color_intensity": &data.ColorIntensity,
63
		"aroma_intensity": &data.AromaIntensity, "nose_complexity": &data.NoseComplexity,
64
	}
65
	for name, slot := range scoreFields {
66
		if v := r.FormValue(name); v != "" {
67
			if n, err := strconv.Atoi(v); err == nil {
68
				*slot = clamp1to5(n)
69
			}
70
		}
71
	}
72
73
	if data.Name == "" {
74
		return nil, errors.New("Name is required")
75
	}
76
	if err := readFormImage(r, data); err != nil {
77
		return nil, err
78
	}
79
	return data, nil
80
}
81
82
func parseWishlistMultipart(r *http.Request) (*wineFormData, error) {
83
	r.Body = http.MaxBytesReader(nil, r.Body, maxUploadBytes)
84
	if err := r.ParseMultipartForm(maxUploadBytes); err != nil {
85
		return nil, err
86
	}
87
	data := &wineFormData{
88
		Sweetness: 3, Acidity: 3, Tannin: 3, Alcohol: 3, Body: 3,
89
		Clarity: 3, ColorIntensity: 3, AromaIntensity: 3, NoseComplexity: 3,
90
	}
91
	data.Name = strings.TrimSpace(r.FormValue("name"))
92
	data.Origin = strings.TrimSpace(r.FormValue("origin"))
93
	data.Grape = strings.TrimSpace(r.FormValue("grape"))
94
	data.Notes = strings.TrimSpace(r.FormValue("notes"))
95
	data.Background = strings.TrimSpace(r.FormValue("background"))
96
	if data.Name == "" {
97
		return nil, errors.New("Name is required")
98
	}
99
	if err := readFormImage(r, data); err != nil {
100
		return nil, err
101
	}
102
	return data, nil
103
}
104
105
func readFormImage(r *http.Request, data *wineFormData) error {
106
	file, _, err := r.FormFile("image")
107
	if err != nil {
108
		if errors.Is(err, http.ErrMissingFile) {
109
			return nil
110
		}
111
		return nil
112
	}
113
	defer file.Close()
114
	raw, err := io.ReadAll(file)
115
	if err != nil {
116
		return err
117
	}
118
	if len(raw) == 0 {
119
		return nil
120
	}
121
	processed, err := processImage(raw)
122
	if err != nil {
123
		return err
124
	}
125
	data.Image = processed
126
	data.ImageMime = "image/jpeg"
127
	return nil
128
}
129
130
func formToInput(f *wineFormData) WineInput {
131
	return WineInput{
132
		Name: f.Name, Origin: f.Origin, Grape: f.Grape, Notes: f.Notes,
133
		Background: f.Background,
134
		Sweetness:  f.Sweetness, Acidity: f.Acidity, Tannin: f.Tannin,
135
		Alcohol: f.Alcohol, Body: f.Body,
136
		Clarity: f.Clarity, ColorIntensity: f.ColorIntensity,
137
		AromaIntensity: f.AromaIntensity, NoseComplexity: f.NoseComplexity,
138
	}
139
}
140
141
var _ multipart.File = (multipart.File)(nil)