diff --git a/main.go b/main.go index 9808d5c51465ae1cc64643a79a39cb72977063a6..a02a9efc1de9657df5021dec9817cae0b7dfd216 100644 --- a/main.go +++ b/main.go @@ -2,8 +2,10 @@ package main import ( "embed" + "errors" "fmt" "html/template" + "io/ioutil" "log" "net/http" "strconv" @@ -19,6 +21,11 @@ var templates = template.Must(template.ParseFS(templateFS, "templates/*.tpl")) //go:embed static/* var staticFS embed.FS +func httpError(w http.ResponseWriter, msg string, err error, status int) { + log.Println(msg) + http.Error(w, msg+": "+err.Error(), status) +} + func main() { db, err := sqlx.Open("sqlite", "vino.sqlite") if err != nil { @@ -39,7 +46,7 @@ func main() { if r.Method == "GET" { wines, err := ListWines(r.Context(), db) if err != nil { - http.Error(w, "can't list wines: "+err.Error(), http.StatusInternalServerError) + httpError(w, "can't list wines", err, http.StatusInternalServerError) return } @@ -58,7 +65,7 @@ func main() { } if r.Method != "POST" { - http.Error(w, "invalid method:"+r.Method, http.StatusMethodNotAllowed) + httpError(w, "invalid method", errors.New(r.Method), http.StatusMethodNotAllowed) return } @@ -66,37 +73,57 @@ func main() { name := r.FormValue("name") if name == "" { - http.Error(w, "name empty or missing", http.StatusBadRequest) + httpError(w, "bad name", errors.New("empty or missing"), http.StatusBadRequest) return } if len(name) > 80 { - http.Error(w, "name too long, max length is 80", http.StatusBadRequest) + httpError(w, "bad name", errors.New("name too long, max length is 80"), http.StatusBadRequest) return } ratingVal := r.FormValue("rating") - rating, err := strconv.Atoi(ratingVal) + + var rating int + if ratingVal != "" { + rating, err = strconv.Atoi(ratingVal) + if err != nil { + httpError(w, "can't convert rating", err, http.StatusBadRequest) + return + } + } + + picFile, picHdr, err := r.FormFile("picture") + if err != nil { + httpError(w, "can't load picture file", err, http.StatusInternalServerError) + return + } + defer picFile.Close() + + log.Println("picture", picHdr.Size, picHdr.Header.Get("Content-Type")) + + picData, err := ioutil.ReadAll(picFile) if err != nil { - http.Error(w, "can't convert rating:"+err.Error(), http.StatusBadRequest) + httpError(w, "can't read picture file", err, http.StatusInternalServerError) return } vino := Vino{ - Name: name, - Rating: rating, + Name: name, + Rating: rating, + Picture: picData, } err = vino.Store(r.Context(), db) if err != nil { - http.Error(w, fmt.Sprintf("can't store wine %q: %s", vino, err), http.StatusInternalServerError) + httpError(w, fmt.Sprintf("can't store wine %q", vino), err, http.StatusInternalServerError) return } http.Redirect(w, r, "/", http.StatusSeeOther) // TODO: Is this the correct status? }) - const listenAddr = "127.0.0.1:7878" + const listenAddr = ":7878" log.Printf("here we go, listening on http://%s", listenAddr) diff --git a/templates/index.tpl b/templates/index.tpl index 064700604afc7e237d03fea1a2faae879c477a82..bd53485989abb95140f5685e4bf68a65214bf4bc 100644 --- a/templates/index.tpl +++ b/templates/index.tpl @@ -11,11 +11,11 @@ <body> The DB contains {{ .Wines | len }} wine(s) right now: - <form method="POST" action="/add"> - <input type="text" name="name" placeholder="Name"></input> + <form method="POST" enctype="multipart/form-data" action="/add"> + <input type="text" name="name" placeholder="Name" required></input> <input type="number" name="rating" placeholder="Rating"></input> - - <span class="todo">TODO: Image</todo> + <br/> + <input type="file" name="picture" accept="image/png, image/jpeg"></input> <button type="submit">+</button> </form>