Skip to content
Snippets Groups Projects
main.go 2.92 KiB
Newer Older
gbe's avatar
gbe committed
package main

import (
	"embed"
gbe's avatar
gbe committed
	"errors"
gbe's avatar
gbe committed
	"fmt"
gbe's avatar
gbe committed
	"html/template"
gbe's avatar
gbe committed
	"io/ioutil"
gbe's avatar
gbe committed
	"log"
	"net/http"
gbe's avatar
gbe committed
	"strconv"
gbe's avatar
gbe committed

	"github.com/jmoiron/sqlx"
	_ "modernc.org/sqlite" // Imported for side effects: registers DB driver
gbe's avatar
gbe committed
)

//go:embed templates/*.tpl
var templateFS embed.FS
var templates = template.Must(template.ParseFS(templateFS, "templates/*.tpl"))

//go:embed static/*
var staticFS embed.FS

gbe's avatar
gbe committed
func httpError(w http.ResponseWriter, msg string, err error, status int) {
	log.Println(msg)
	http.Error(w, msg+": "+err.Error(), status)
}

gbe's avatar
gbe committed
func main() {
gbe's avatar
gbe committed
	db, err := sqlx.Open("sqlite", "vino.sqlite")
	if err != nil {
		log.Fatalln("can't open DB:", err)
	}
	defer db.Close()

	err = initDB(db)
	if err != nil {
		log.Fatalln("can't initialize DB:", err)
	}

gbe's avatar
gbe committed
	http.Handle("/static/", http.FileServer(http.FS(staticFS)))

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		log.Println("handling", r.Method, r.URL, "from", r.RemoteAddr)

		if r.Method == "GET" {
gbe's avatar
gbe committed
			wines, err := ListWines(r.Context(), db)
			if err != nil {
gbe's avatar
gbe committed
				httpError(w, "can't list wines", err, http.StatusInternalServerError)
gbe's avatar
gbe committed
				return
			}

gbe's avatar
gbe committed
			w.Header().Add("content-type", "text/html")

gbe's avatar
gbe committed
			data := struct {
				Wines []Vino
			}{wines}

			err = templates.ExecuteTemplate(w, "index.tpl", data)
gbe's avatar
gbe committed
			if err != nil {
				log.Println("can't execute index template:", err)
			}

			return
		}
gbe's avatar
gbe committed

		if r.Method != "POST" {
gbe's avatar
gbe committed
			httpError(w, "invalid method", errors.New(r.Method), http.StatusMethodNotAllowed)
gbe's avatar
gbe committed
			return
		}

		// TODO: load/store image

		name := r.FormValue("name")
		if name == "" {
gbe's avatar
gbe committed
			httpError(w, "bad name", errors.New("empty or missing"), http.StatusBadRequest)
gbe's avatar
gbe committed
			return
		}

		if len(name) > 80 {
gbe's avatar
gbe committed
			httpError(w, "bad name", errors.New("name too long, max length is 80"), http.StatusBadRequest)
gbe's avatar
gbe committed
		ratingVal := r.FormValue("rating")
gbe's avatar
gbe committed

		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)
gbe's avatar
gbe committed
		if err != nil {
gbe's avatar
gbe committed
			httpError(w, "can't read picture file", err, http.StatusInternalServerError)
gbe's avatar
gbe committed
			return
		}

		vino := Vino{
gbe's avatar
gbe committed
			Name:    name,
			Rating:  rating,
			Picture: picData,
gbe's avatar
gbe committed
		}

		err = vino.Store(r.Context(), db)
		if err != nil {
gbe's avatar
gbe committed
			httpError(w, fmt.Sprintf("can't store wine %q", vino), err, http.StatusInternalServerError)
gbe's avatar
gbe committed
			return
		}

		http.Redirect(w, r, "/", http.StatusSeeOther) // TODO: Is this the correct status?
gbe's avatar
gbe committed
	})

gbe's avatar
gbe committed
	const listenAddr = ":7878"
gbe's avatar
gbe committed

	log.Printf("here we go, listening on http://%s", listenAddr)

gbe's avatar
gbe committed
	err = http.ListenAndServe(listenAddr, nil)
gbe's avatar
gbe committed
	if err != nil {
gbe's avatar
gbe committed
		log.Fatalln("http handler failed:", err)
gbe's avatar
gbe committed
	}
}