...fetching
- {:then data} - {#if data} -Status
-{data.status}
-No data yet
- {/if} - {:catch errror} -{error.message}
- {/await} +From e37c73e33a4aaf7fb8d25b5af03627f20bcda19f Mon Sep 17 00:00:00 2001
From: Doog <157747121+doogongithub@users.noreply.github.com>
Date: Sat, 24 Feb 2024 20:08:35 -0500
Subject: add gitignore
---
.gitignore | 49 +++++++++++++++
api/go.mod | 6 +-
api/go.sum | 9 +++
api/lib/models.go | 23 +++++++
api/main.go | 127 ++++++++++++++++++++++++++++++++------
db/scripts/water_init.sql | 10 +--
db/water.sqlite3 | Bin 40960 -> 24576 bytes
fe/src/App.svelte | 147 +++-----------------------------------------
fe/src/app.css | 2 +-
fe/src/lib/DataView.svelte | 67 ++++++++++++++++++++
fe/src/lib/Layout.svelte | 57 +++++++++++++++++
fe/src/lib/LoginForm.svelte | 64 +++++++++++++++++++
fe/src/lib/Table.svelte | 61 +++++++++++++++---
fe/src/stores/auth.ts | 48 +++++++++++++++
fe/svelte.config.js | 1 +
15 files changed, 500 insertions(+), 171 deletions(-)
create mode 100644 .gitignore
create mode 100644 api/lib/models.go
create mode 100644 fe/src/lib/DataView.svelte
create mode 100644 fe/src/lib/Layout.svelte
create mode 100644 fe/src/lib/LoginForm.svelte
create mode 100644 fe/src/stores/auth.ts
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4e424ad
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,49 @@
+# If you prefer the allow list template instead of the deny list, see community template:
+# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
+#
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Dependency directories (remove the comment below to include it)
+vendor/
+
+# Go workspace file
+go.work
+
+
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+.pnpm-debug.log*
+
+# Dependency directories
+node_modules/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional REPL history
+.node_repl_history
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+
diff --git a/api/go.mod b/api/go.mod
index 02f7c09..08ad9e1 100644
--- a/api/go.mod
+++ b/api/go.mod
@@ -3,12 +3,16 @@ module water/api
go 1.18
require (
+ github.com/gin-gonic/gin v1.9.1
+ github.com/mattn/go-sqlite3 v1.14.22
+)
+
+require (
github.com/bytedance/sonic v1.11.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
- github.com/gin-gonic/gin v1.9.1 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.18.0 // indirect
diff --git a/api/go.sum b/api/go.sum
index eff6af1..5174feb 100644
--- a/api/go.sum
+++ b/api/go.sum
@@ -10,6 +10,7 @@ github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLI
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
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/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
@@ -17,6 +18,7 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
+github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
@@ -25,6 +27,7 @@ github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtP
github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
@@ -36,6 +39,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -43,6 +48,7 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
+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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@@ -52,6 +58,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+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/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
@@ -70,8 +77,10 @@ golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/api/lib/models.go b/api/lib/models.go
new file mode 100644
index 0000000..92e5703
--- /dev/null
+++ b/api/lib/models.go
@@ -0,0 +1,23 @@
+package models
+
+import "time"
+
+type Statistic struct {
+ ID int64 `json:"id"`
+ Date time.Time `json:"date"`
+ UserID int64 `json:"user_id"`
+ Quantity int `json:"quantity"`
+}
+
+type User struct {
+ ID int64
+ Name string
+}
+
+type Token struct {
+ ID int64
+ UserID int64
+ Token string
+ CreatedAt time.Time
+ ExpiredAt time.Time
+}
diff --git a/api/main.go b/api/main.go
index ebae5d1..292a5f9 100644
--- a/api/main.go
+++ b/api/main.go
@@ -4,8 +4,13 @@ import (
"net/http"
"crypto/rand"
"encoding/base64"
+ "database/sql"
+ "strings"
+ "errors"
"github.com/gin-gonic/gin"
+ _ "github.com/mattn/go-sqlite3"
+ "water/api/lib"
)
func CORSMiddleware() gin.HandlerFunc {
@@ -30,6 +35,44 @@ func generateToken() string {
return base64.StdEncoding.EncodeToString(token)
}
+func establishDBConnection() *sql.DB {
+ db, err := sql.Open("sqlite3", "../db/water.sqlite3")
+ if err != nil {
+ panic(err)
+ }
+ return db
+}
+
+func checkForTokenInContext(c *gin.Context) (string, error) {
+ authorizationHeader := c.GetHeader("Authorization")
+ if authorizationHeader == "" {
+ return "", errors.New("Authorization header is missing")
+ }
+
+ parts := strings.Split(authorizationHeader, " ")
+
+ if len(parts) != 2 || parts[0] != "Bearer" {
+ return "", errors.New("Invalid Authorization header format")
+ }
+
+ return parts[1], nil
+}
+
+
+func TokenRequired() gin.HandlerFunc {
+ return func(c *gin.Context) {
+ _, err := checkForTokenInContext(c)
+
+ if err != nil {
+ c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
+ c.Abort()
+ return
+ }
+
+ c.Next()
+ }
+}
+
type User struct {
Username string
Password string
@@ -44,6 +87,8 @@ func setupRouter() *gin.Engine {
// gin.DisableConsoleColor()
r := gin.Default()
r.Use(CORSMiddleware())
+ r.Use(gin.Logger())
+ r.Use(gin.Recovery())
api := r.Group("api/v1")
@@ -68,26 +113,70 @@ func setupRouter() *gin.Engine {
})
stats := api.Group("stats")
+ stats.Use(TokenRequired())
+ {
+ stats.GET("/", func(c *gin.Context) {
+ db := establishDBConnection()
+ defer db.Close()
+
+ rows, err := db.Query("SELECT * FROM statistics");
+ if err != nil {
+ c.JSON(500, gin.H{"error": err.Error()})
+ return
+ }
+ defer rows.Close()
+
+ var data []models.Statistic
+ for rows.Next() {
+ var stat models.Statistic
+ if err := rows.Scan(&stat.ID, &stat.Date, &stat.UserID, &stat.Quantity); err != nil {
+ c.JSON(500, gin.H{"error": err.Error()})
+ return
+ }
+ data = append(data, stat)
+ }
+
+ c.JSON(http.StatusOK, data)
+ })
+
+ stats.POST("/", func(c *gin.Context) {
+ var stat models.Statistic
+
+ if err := c.BindJSON(&stat); err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ return
+ }
+
+ db := establishDBConnection()
+ defer db.Close()
+
+ result, err := db.Exec("INSERT INTO statistics (date, user_id, quantity) values (?, ?, ?)", stat.Date, stat.UserID, stat.Quantity)
+
+ if err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ }
+
+ id, err := result.LastInsertId()
+ if err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ }
+
+ c.JSON(http.StatusCreated, gin.H{"status": "created", "id": id})
+ })
+
+ stats.GET("/:uuid", func(c *gin.Context) {
+ c.JSON(http.StatusOK, gin.H{"status": "ok", "uuid": c.Param("uuid")})
+ })
+
+ stats.PATCH("/:uuid", func(c *gin.Context) {
+ c.JSON(http.StatusNoContent, gin.H{"status": "No Content"})
+ })
+
+ stats.DELETE("/:uuid", func(c *gin.Context) {
+ c.JSON(http.StatusNoContent, gin.H{"status": "No Content"})
+ })
+ }
- stats.GET("/", func(c *gin.Context) {
- c.JSON(http.StatusOK, gin.H{"status": "ok"})
- })
-
- stats.POST("/", func(c *gin.Context) {
- c.JSON(http.StatusCreated, gin.H{"status": "created"})
- })
-
- stats.GET("/:uuid", func(c *gin.Context) {
- c.JSON(http.StatusOK, gin.H{"status": "ok", "uuid": c.Param("uuid")})
- })
-
- stats.PATCH("/:uuid", func(c *gin.Context) {
- c.JSON(http.StatusNoContent, gin.H{"status": "No Content"})
- })
-
- stats.DELETE("/:uuid", func(c *gin.Context) {
- c.JSON(http.StatusNoContent, gin.H{"status": "No Content"})
- })
return r
}
diff --git a/db/scripts/water_init.sql b/db/scripts/water_init.sql
index d7b912a..0751c41 100644
--- a/db/scripts/water_init.sql
+++ b/db/scripts/water_init.sql
@@ -1,13 +1,13 @@
-- user table for users.
CREATE TABLE IF NOT EXISTS Users (
- id INT PRIMARY KEY,
+ id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
UNIQUE(name)
);
-- statistics table for users to log their consumption
CREATE TABLE IF NOT EXISTS Statistics (
- id INT PRIMARY KEY,
+ id INTEGER PRIMARY KEY,
date DATETIME NOT NULL,
user_id INT NOT NULL,
quantity INT
@@ -15,7 +15,7 @@ CREATE TABLE IF NOT EXISTS Statistics (
-- preferences table for a user.
CREATE TABLE IF NOT EXISTS Preferences (
- id INT PRIMARY KEY,
+ id INTEGER PRIMARY KEY,
color TEXT NOT NULL DEFAULT "#000000",
user_id INT NOT NULL,
size_id INT NOT NULL DEFAULT 1,
@@ -25,8 +25,8 @@ CREATE TABLE IF NOT EXISTS Preferences (
-- lookup table for sizes.
CREATE TABLE IF NOT EXISTS Sizes (
- id INT PRIMARY KEY,
- size INT NOT NULL
+ id INTEGER PRIMARY KEY,
+ size INT NOT NULL,
unit TEXT DEFAULT "oz"
);
diff --git a/db/water.sqlite3 b/db/water.sqlite3
index 97f9214..c800708 100644
Binary files a/db/water.sqlite3 and b/db/water.sqlite3 differ
diff --git a/fe/src/App.svelte b/fe/src/App.svelte
index cc4e594..8811c52 100644
--- a/fe/src/App.svelte
+++ b/fe/src/App.svelte
@@ -1,146 +1,19 @@
...fetching Status {data.status} No data yet {error.message}
-
-
- {:else}
-
- Data Header - | + {#each getDataKeys(data) as header} +{header} | + {/each}
---|---|
Data | + {#each getRow(row) as datum} + +{formatDatum(datum)} | + {/each}