From 677b4277a653578a1f7f2600450cd2adc654545a Mon Sep 17 00:00:00 2001 From: Fluffy-Bean Date: Sun, 5 May 2024 17:51:34 +0100 Subject: [PATCH] Start building out the backend with database --- .gitignore | 7 +++++ api/api.go | 29 ++++++++++++++++-- api/test_data.go | 59 ------------------------------------ cmd/cmd.go | 2 ++ cmd/database.go | 58 ++++++++++++++++++++++++++++++++++++ database/database.go | 40 +++++++++++++++++++++++++ database/tables.go | 16 ++++++++++ database/test_data.go | 69 +++++++++++++++++++++++++++++++++++++++++++ go.mod | 7 ++++- go.sum | 13 ++++++++ main.go | 4 +++ 11 files changed, 241 insertions(+), 63 deletions(-) delete mode 100644 api/test_data.go create mode 100644 cmd/database.go create mode 100644 database/database.go create mode 100644 database/tables.go create mode 100644 database/test_data.go diff --git a/.gitignore b/.gitignore index 8a06098..d259e0b 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,10 @@ go.work build-*.log build/ main + +# Databases +*.db +*.db-shm +*.db-wal +*.sqlite +*.sqlite3 diff --git a/api/api.go b/api/api.go index ea584af..52ea8f2 100644 --- a/api/api.go +++ b/api/api.go @@ -3,7 +3,9 @@ package api import ( "net/http" + db "github.com/Fluffy-Bean/TastyBites/database" "github.com/Fluffy-Bean/TastyBites/front" + sb "github.com/huandu/go-sqlbuilder" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" ) @@ -24,9 +26,30 @@ func Serve(c Config) { r.StaticFS("/", front.DistDir) - api := r.Group("/api") - api.GET("/items", func(e echo.Context) error { - return e.JSON(http.StatusOK, sampleData) + apiGroup := r.Group("/api") + apiGroup.GET("/items", func(e echo.Context) error { + builder := db.ItemStruct.SelectFrom("Item").Select("*") + query, args := builder.BuildWithFlavor(sb.SQLite) + + rows, err := db.Conn.Query(query, args...) + defer rows.Close() + if err != nil { + r.Logger.Fatal(err) + return e.String(http.StatusInternalServerError, "Could not query for data") + } + + var items []db.Item + for rows.Next() { + var item db.Item + err := rows.Scan(db.ItemStruct.Addr(&item)...) + if err != nil { + r.Logger.Fatal(err) + return e.String(http.StatusInternalServerError, "Could not scan row") + } + items = append(items, item) + } + + return e.JSON(http.StatusOK, items) }) r.HideBanner = true diff --git a/api/test_data.go b/api/test_data.go deleted file mode 100644 index 7b2557a..0000000 --- a/api/test_data.go +++ /dev/null @@ -1,59 +0,0 @@ -package api - -type menuItem struct { - Name string `json:"name"` - Price float32 `json:"price"` - Labels []string `json:"labels"` - Image string `json:"image"` -} - -var sampleData = []menuItem{ - { - Name: "Bar of Soap", - Price: 69.99, - Labels: []string{"vegan", "spicy"}, - }, - { - Name: "Sock", - Price: 21, - Labels: []string{"vegan", "fish", "nut", "spicy"}, - }, - { - Name: "Brick", - Price: 0, - Labels: []string{"spicy"}, - }, - { - Name: "Toast", - Price: 4382749832743, - Labels: []string{"gluten"}, - }, - { - Name: "water", - Price: 1, - Labels: []string{"fish"}, - }, - { - Name: "half eaten mouldy bread", - Price: -9999, - Labels: []string{"nut"}, - }, - { - Name: "GwaGwa", - Price: 69, - Labels: []string{"nut"}, - Image: "/dab.jpg", - }, - { - Name: "Hogermellon", - Price: 1111, - Labels: []string{"fish"}, - Image: "/wathog.jpg", - }, - { - Name: "Blue HOGGGGG", - Price: 0, - Labels: []string{"nut", "gluten", "spicy"}, - Image: "/sonichog.jpg", - }, -} diff --git a/cmd/cmd.go b/cmd/cmd.go index ef379c1..8ca851c 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -14,6 +14,8 @@ func Parse() { switch os.Args[1] { case "run": run(os.Args[2:]) + case "migrate": + migrate(os.Args[2:]) case "-h": case "--help": fmt.Println("Available commands are:") diff --git a/cmd/database.go b/cmd/database.go new file mode 100644 index 0000000..0b6c2c2 --- /dev/null +++ b/cmd/database.go @@ -0,0 +1,58 @@ +package cmd + +import ( + "flag" + "fmt" + "log" + "os" + + db "github.com/Fluffy-Bean/TastyBites/database" + sb "github.com/huandu/go-sqlbuilder" +) + +func migrate(flags []string) { + + cmd := flag.NewFlagSet("migrate", flag.ExitOnError) + + tables := cmd.Bool("tables", false, "Create tables") + verbose := cmd.Bool("v", false, "Verbose") + + err := cmd.Parse(flags) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + if !*tables { + builder := db.ItemStruct.InsertInto("Item") + for _, item := range db.SampleData { + builder.Values(item.UUID, item.Name, item.Price, item.Description) + } + query, args := builder.Build() + + _, err = db.Conn.Exec(query, args...) + if err != nil { + log.Fatal(err) + } else { + fmt.Println("Success") + } + } else { + itemTable := sb.CreateTable("Item").IfNotExists() + itemTable.Define("uuid", "TEXT", "NOT NULL", "PRIMARY KEY") + itemTable.Define("name", "TEXT", "NOT NULL") + itemTable.Define("price", "INTEGER", "NOT NULL") + itemTable.Define("description", "TEXT") + query, args := itemTable.BuildWithFlavor(sb.SQLite) + + if *verbose { + fmt.Println(query, args) + } + + _, err := db.Conn.Exec(query, args...) + if err != nil { + log.Fatal(err) + } else { + fmt.Println("Success") + } + } +} diff --git a/database/database.go b/database/database.go new file mode 100644 index 0000000..ef0fef8 --- /dev/null +++ b/database/database.go @@ -0,0 +1,40 @@ +package database + +import ( + "database/sql" + "github.com/mattn/go-sqlite3" + "log" + + _ "github.com/mattn/go-sqlite3" +) + +var Conn *sql.DB + +func Open() { + var err error + + sql.Register("sqlite3_with_extensions", + &sqlite3.SQLiteDriver{ + Extensions: []string{ + "sqlite3_mod_regexp", + }, + }) + + Conn, err = sql.Open("sqlite3", "tastybites.db?_journal_mode=WAL") + if err != nil { + log.Fatal("Error opening connection: ", err) + } + + //Set the connection to use WAL mode + _, err = Conn.Exec("PRAGMA journal_mode=WAL;") + if err != nil { + log.Fatal(err) + } +} + +func Close() { + err := Conn.Close() + if err != nil { + log.Fatal(err) + } +} diff --git a/database/tables.go b/database/tables.go new file mode 100644 index 0000000..1ea0f17 --- /dev/null +++ b/database/tables.go @@ -0,0 +1,16 @@ +package database + +import ( + sb "github.com/huandu/go-sqlbuilder" +) + +type Item struct { + UUID string `json:"uuid" db:"uuid"` + Name string `json:"name" db:"name"` + Price int64 `json:"price" db:"price"` + Description string `json:"description,omitempty" db:"description"` + //Labels []string `json:"labels,omitempty" db:"labels"` + //Images []string `json:"images,omitempty" db:"images"` +} + +var ItemStruct = sb.NewStruct(new(Item)) diff --git a/database/test_data.go b/database/test_data.go new file mode 100644 index 0000000..1e47cdd --- /dev/null +++ b/database/test_data.go @@ -0,0 +1,69 @@ +package database + +var SampleData = []Item{ + { + UUID: "soap", + Name: "Bar of Soap", + Price: 6999, + //Labels: []string{"vegan", "spicy"}, + Description: "Example", + }, + { + UUID: "sock", + Name: "Sock", + Price: 21, + //Labels: []string{"vegan", "fish", "nut", "spicy"}, + Description: "Example", + }, + { + UUID: "brick", + Name: "Brick", + Price: 0, + //Labels: []string{"spicy"}, + Description: "Example", + }, + { + UUID: "toast", + Name: "Toast", + Price: 4382749832743, + //Labels: []string{"gluten"}, + Description: "Example", + }, + { + UUID: "water", + Name: "water", + Price: 1, + //Labels: []string{"fish"}, + Description: "Example", + }, + { + UUID: "mouldy_bread", + Name: "half eaten mouldy bread", + Price: -99, + //Labels: []string{"nut"}, + Description: "Example", + }, + { + UUID: "gwagwa", + Name: "GwaGwa", + Price: 69, + //Labels: []string{"nut"}, + //Images: []string{"/dab.jpg"}, + }, + { + UUID: "hogmelon", + Name: "Hogermellon", + Price: 1111, + //Labels: []string{"fish"}, + //Images: []string{"/wathog.jpg"}, + Description: "Example", + }, + { + UUID: "bluhog", + Name: "Blue HOGGGGG", + Price: 0, + //Labels: []string{"nut", "gluten", "spicy"}, + //Images: []string{"/sonichog.jpg"}, + Description: "Example", + }, +} diff --git a/go.mod b/go.mod index c2383e0..0e92288 100644 --- a/go.mod +++ b/go.mod @@ -2,10 +2,15 @@ module github.com/Fluffy-Bean/TastyBites go 1.22.1 -require github.com/labstack/echo/v4 v4.12.0 +require ( + github.com/huandu/go-sqlbuilder v1.27.1 + github.com/labstack/echo/v4 v4.12.0 + github.com/mattn/go-sqlite3 v1.14.22 +) require ( github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/huandu/xstrings v1.3.2 // indirect github.com/labstack/gommon v0.4.2 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect diff --git a/go.sum b/go.sum index 22a904a..8fe7f60 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,14 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= +github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= +github.com/huandu/go-sqlbuilder v1.27.1 h1:7UU/3EMIQYYX8wn+L7BNcGVz1aEs5TPNOVFd7ryrPos= +github.com/huandu/go-sqlbuilder v1.27.1/go.mod h1:nUVmMitjOmn/zacMLXT0d3Yd3RHoO2K+vy906JzqxMI= +github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= +github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= @@ -11,8 +18,12 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -31,5 +42,7 @@ golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index f83b60a..51fb831 100644 --- a/main.go +++ b/main.go @@ -2,8 +2,12 @@ package main import ( "github.com/Fluffy-Bean/TastyBites/cmd" + "github.com/Fluffy-Bean/TastyBites/database" ) func main() { + // Open the DB here, because I'm gamer + database.Open() cmd.Parse() + database.Close() }