aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZach Berwaldt <zberwaldt@tutamail.com>2024-03-18 21:27:24 -0400
committerZach Berwaldt <zberwaldt@tutamail.com>2024-03-18 21:27:24 -0400
commiteb51bdbfef8c2aacf0fdfde279a40de7f74c8d86 (patch)
treef8fdfb4e28fa271eda3027d84843e4b476831cdd
parent5ddcd1cf663cc732819cfcb66f2ff6b1bbeb0d47 (diff)
clean up, add better error handling
-rwxr-xr-xapi/bin/waterbin0 -> 15831520 bytes
-rw-r--r--api/internal/controllers/auth.go21
-rw-r--r--api/internal/controllers/preferences.go13
-rw-r--r--api/internal/controllers/stats.go27
-rw-r--r--api/internal/controllers/user.go14
-rw-r--r--api/internal/database/database.go18
-rwxr-xr-xapi/mainbin0 -> 17796784 bytes
-rw-r--r--api/water-api.nginx.conf8
-rw-r--r--api/water.service14
-rw-r--r--db/README.md10
-rw-r--r--db/scripts/init.sh6
-rw-r--r--fe/index.html2
-rw-r--r--fe/src/utils.ts2
-rw-r--r--fe/water.nginx.conf12
14 files changed, 114 insertions, 33 deletions
diff --git a/api/bin/water b/api/bin/water
new file mode 100755
index 0000000..b311c73
--- /dev/null
+++ b/api/bin/water
Binary files differ
diff --git a/api/internal/controllers/auth.go b/api/internal/controllers/auth.go
index ab2fbbb..b06c6ef 100644
--- a/api/internal/controllers/auth.go
+++ b/api/internal/controllers/auth.go
@@ -5,21 +5,21 @@ import (
5 "database/sql" 5 "database/sql"
6 "encoding/base64" 6 "encoding/base64"
7 "errors" 7 "errors"
8 "github.com/gin-gonic/gin"
9 "net/http" 8 "net/http"
10 "water/api/internal/models" 9 "water/api/internal/models"
11 10
11 "github.com/gin-gonic/gin"
12
13 "water/api/internal/database"
14
12 _ "github.com/mattn/go-sqlite3" 15 _ "github.com/mattn/go-sqlite3"
13 "golang.org/x/crypto/bcrypt" 16 "golang.org/x/crypto/bcrypt"
14 "water/api/internal/database"
15) 17)
16 18
17
18
19// AuthHandler is a function that handles users' authentication. It checks if the request 19// AuthHandler is a function that handles users' authentication. It checks if the request
20// has valid credentials, authenticates the user and sets the user's session. 20// has valid credentials, authenticates the user and sets the user's session.
21// If the authentication is successful, it will allow the user to access protected routes. 21// If the authentication is successful, it will allow the user to access protected routes.
22func AuthHandler (c *gin.Context) { 22func AuthHandler(c *gin.Context) {
23 username, password, ok := c.Request.BasicAuth() 23 username, password, ok := c.Request.BasicAuth()
24 if !ok { 24 if !ok {
25 c.Header("WWW-Authenticate", `Basic realm="Please enter your username and password."`) 25 c.Header("WWW-Authenticate", `Basic realm="Please enter your username and password."`)
@@ -27,7 +27,11 @@ func AuthHandler (c *gin.Context) {
27 return 27 return
28 } 28 }
29 29
30 db := database.EstablishDBConnection() 30 db, err := database.EstablishDBConnection()
31 if err != nil {
32 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
33 return
34 }
31 defer func(db *sql.DB) { 35 defer func(db *sql.DB) {
32 err := db.Close() 36 err := db.Close()
33 if err != nil { 37 if err != nil {
@@ -44,6 +48,8 @@ func AuthHandler (c *gin.Context) {
44 if errors.Is(err, sql.ErrNoRows) { 48 if errors.Is(err, sql.ErrNoRows) {
45 c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) 49 c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
46 return 50 return
51 } else {
52 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
47 } 53 }
48 } 54 }
49 55
@@ -64,7 +70,6 @@ func AuthHandler (c *gin.Context) {
64 c.JSON(http.StatusOK, gin.H{"token": apiToken, "user": user, "preferences": preference}) 70 c.JSON(http.StatusOK, gin.H{"token": apiToken, "user": user, "preferences": preference})
65} 71}
66 72
67
68// generateToken is a helper function used in the AuthHandler. It generates a random token for API authentication. 73// generateToken is a helper function used in the AuthHandler. It generates a random token for API authentication.
69// This function creates an empty byte slice of length 32 and fills it with cryptographic random data using the rand.Read function. 74// This function creates an empty byte slice of length 32 and fills it with cryptographic random data using the rand.Read function.
70// If an error occurs during the generation, it will return an empty string. 75// If an error occurs during the generation, it will return an empty string.
@@ -76,4 +81,4 @@ func generateToken() string {
76 return "" 81 return ""
77 } 82 }
78 return base64.StdEncoding.EncodeToString(token) 83 return base64.StdEncoding.EncodeToString(token)
79} \ No newline at end of file 84}
diff --git a/api/internal/controllers/preferences.go b/api/internal/controllers/preferences.go
index a1bcf4f..da52e74 100644
--- a/api/internal/controllers/preferences.go
+++ b/api/internal/controllers/preferences.go
@@ -1,22 +1,27 @@
1package controllers 1package controllers
2 2
3import ( 3import (
4 "github.com/gin-gonic/gin"
5 "net/http"
6 "database/sql" 4 "database/sql"
5 "net/http"
7 "water/api/internal/database" 6 "water/api/internal/database"
8 "water/api/internal/models" 7 "water/api/internal/models"
8
9 "github.com/gin-gonic/gin"
9) 10)
10 11
11func GetSizes(c *gin.Context) { 12func GetSizes(c *gin.Context) {
12 db := database.EstablishDBConnection() 13 db, err := database.EstablishDBConnection()
14 if err != nil {
15 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
16 return
17 }
13 defer func(db *sql.DB) { 18 defer func(db *sql.DB) {
14 err := db.Close() 19 err := db.Close()
15 if err != nil { 20 if err != nil {
16 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) 21 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
17 } 22 }
18 }(db) 23 }(db)
19 24
20 rows, err := db.Query("SELECT id, size, unit FROM Sizes") 25 rows, err := db.Query("SELECT id, size, unit FROM Sizes")
21 if err != nil { 26 if err != nil {
22 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) 27 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
diff --git a/api/internal/controllers/stats.go b/api/internal/controllers/stats.go
index 2234787..889b923 100644
--- a/api/internal/controllers/stats.go
+++ b/api/internal/controllers/stats.go
@@ -2,10 +2,11 @@ package controllers
2 2
3import ( 3import (
4 "database/sql" 4 "database/sql"
5 "github.com/gin-gonic/gin"
6 "net/http" 5 "net/http"
7 "water/api/internal/database" 6 "water/api/internal/database"
8 "water/api/internal/models" 7 "water/api/internal/models"
8
9 "github.com/gin-gonic/gin"
9) 10)
10 11
11// TODO: add comments to all exported members of package. 12// TODO: add comments to all exported members of package.
@@ -13,7 +14,11 @@ import (
13// GetAllStatistics connects to the database and queries for all statistics in the database. 14// GetAllStatistics connects to the database and queries for all statistics in the database.
14// If none have been found it will return an error, otherwise a 200 code is sent along with the list of statistics. 15// If none have been found it will return an error, otherwise a 200 code is sent along with the list of statistics.
15func GetAllStatistics(c *gin.Context) { 16func GetAllStatistics(c *gin.Context) {
16 db := database.EstablishDBConnection() 17 db, err := database.EstablishDBConnection()
18 if err != nil {
19 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
20 return
21 }
17 defer func(db *sql.DB) { 22 defer func(db *sql.DB) {
18 err := db.Close() 23 err := db.Close()
19 if err != nil { 24 if err != nil {
@@ -59,7 +64,11 @@ func PostNewStatistic(c *gin.Context) {
59 return 64 return
60 } 65 }
61 66
62 db := database.EstablishDBConnection() 67 db, err := database.EstablishDBConnection()
68 if err != nil {
69 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
70 return
71 }
63 defer func(db *sql.DB) { 72 defer func(db *sql.DB) {
64 err := db.Close() 73 err := db.Close()
65 if err != nil { 74 if err != nil {
@@ -83,7 +92,11 @@ func PostNewStatistic(c *gin.Context) {
83} 92}
84 93
85func GetWeeklyStatistics(c *gin.Context) { 94func GetWeeklyStatistics(c *gin.Context) {
86 db := database.EstablishDBConnection() 95 db, err := database.EstablishDBConnection()
96 if err != nil {
97 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
98 return
99 }
87 defer func(db *sql.DB) { 100 defer func(db *sql.DB) {
88 err := db.Close() 101 err := db.Close()
89 if err != nil { 102 if err != nil {
@@ -118,7 +131,11 @@ func GetWeeklyStatistics(c *gin.Context) {
118} 131}
119 132
120func GetDailyUserStatistics(c *gin.Context) { 133func GetDailyUserStatistics(c *gin.Context) {
121 db := database.EstablishDBConnection() 134 db, err := database.EstablishDBConnection()
135 if err != nil {
136 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
137 return
138 }
122 defer func(db *sql.DB) { 139 defer func(db *sql.DB) {
123 err := db.Close() 140 err := db.Close()
124 if err != nil { 141 if err != nil {
diff --git a/api/internal/controllers/user.go b/api/internal/controllers/user.go
index dbb09cf..fa9617a 100644
--- a/api/internal/controllers/user.go
+++ b/api/internal/controllers/user.go
@@ -15,7 +15,11 @@ func GetUser(c *gin.Context) {
15 c.JSON(http.StatusOK, gin.H{"message": "User found"}) 15 c.JSON(http.StatusOK, gin.H{"message": "User found"})
16} 16}
17func GetUserPreferences(c *gin.Context) { 17func GetUserPreferences(c *gin.Context) {
18 db := database.EstablishDBConnection() 18 db, err := database.EstablishDBConnection()
19 if err != nil {
20 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
21 return
22 }
19 defer func(db *sql.DB) { 23 defer func(db *sql.DB) {
20 err := db.Close() 24 err := db.Close()
21 if err != nil { 25 if err != nil {
@@ -41,7 +45,11 @@ func GetUserPreferences(c *gin.Context) {
41} 45}
42 46
43func UpdateUserPreferences(c *gin.Context) { 47func UpdateUserPreferences(c *gin.Context) {
44 db := database.EstablishDBConnection() 48 db, err := database.EstablishDBConnection()
49 if err != nil {
50 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
51 return
52 }
45 defer func(db *sql.DB) { 53 defer func(db *sql.DB) {
46 err := db.Close() 54 err := db.Close()
47 if err != nil { 55 if err != nil {
@@ -58,7 +66,7 @@ func UpdateUserPreferences(c *gin.Context) {
58 66
59 log.Printf("newPreferences: %v", newPreferences) 67 log.Printf("newPreferences: %v", newPreferences)
60 68
61 _, err := db.Exec("UPDATE Preferences SET color = ?, size_id = ? WHERE id = ?", newPreferences.Color, newPreferences.SizeID, newPreferences.ID) 69 _, err = db.Exec("UPDATE Preferences SET color = ?, size_id = ? WHERE id = ?", newPreferences.Color, newPreferences.SizeID, newPreferences.ID)
62 if err != nil { 70 if err != nil {
63 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) 71 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
64 return 72 return
diff --git a/api/internal/database/database.go b/api/internal/database/database.go
index 1866655..1a55127 100644
--- a/api/internal/database/database.go
+++ b/api/internal/database/database.go
@@ -2,23 +2,29 @@ package database
2 2
3import ( 3import (
4 "database/sql" 4 "database/sql"
5 _ "github.com/mattn/go-sqlite3" 5 "fmt"
6 "log" 6 "log"
7 "path/filepath" 7 "path/filepath"
8 "water/api/internal/config" 8 "water/api/internal/config"
9
10 _ "github.com/mattn/go-sqlite3"
9) 11)
10 12
11func EstablishDBConnection() *sql.DB { 13func EstablishDBConnection() (*sql.DB, error) {
12 c, err := config.Load() 14 c, err := config.Load()
15 if err != nil {
16 return nil, fmt.Errorf("error while reading config file: %w", err)
17 }
13 18
14 driver := c.GetString("DB_DRIVER") 19 driver := c.GetString("DB_DRIVER")
15 path, err := filepath.Abs(c.GetString("DB_PATH")) 20 path, err := filepath.Abs(c.GetString("DB_PATH"))
21 log.Println("my db path: ", path)
16 if err != nil { 22 if err != nil {
17 log.Fatal("There was and error getting the absolute path of the database.") 23 return nil, fmt.Errorf("failed to get absolute path of the database: %w", err)
18 } 24 }
19 db, err := sql.Open(driver, path) 25 db, err := sql.Open(driver, path)
20 if err != nil { 26 if err != nil {
21 panic(err) 27 return nil, fmt.Errorf("error while opening the database: %w", err)
22 } 28 }
23 return db 29 return db, nil
24} \ No newline at end of file 30}
diff --git a/api/main b/api/main
new file mode 100755
index 0000000..1eeaf51
--- /dev/null
+++ b/api/main
Binary files differ
diff --git a/api/water-api.nginx.conf b/api/water-api.nginx.conf
new file mode 100644
index 0000000..73b0d7c
--- /dev/null
+++ b/api/water-api.nginx.conf
@@ -0,0 +1,8 @@
1server {
2 listen 443 ssl;
3 listen [::]:443 ssl;
4 server_name water-api.example.com;
5 location / {
6 proxy_pass http://localhost:8888;
7 }
8}
diff --git a/api/water.service b/api/water.service
new file mode 100644
index 0000000..eb8a779
--- /dev/null
+++ b/api/water.service
@@ -0,0 +1,14 @@
1[Unit]
2Description=Water is a backend for tracking water consumption
3StartLimitIntervalSec=600
4StartLimitBurst=2
5
6[Service]
7WorkingDirectory=/path/to/water/binary
8User=water
9Restart=on-failure
10RestartSec=30
11PermissionsStartOnly=true
12
13[Install]
14WantedBy=multi-user.target
diff --git a/db/README.md b/db/README.md
index f36e555..1d5ad71 100644
--- a/db/README.md
+++ b/db/README.md
@@ -1,3 +1,11 @@
1# The Database 1# The Database
2 2
3This document describes how to set up the database for your instance of the water application. \ No newline at end of file 3This document describes how to set up the database for your instance of the water application.
4
5## Setup
6
7The first step is to decide where you are going to keep your database. Once you have decided on where that is going to be add it to your environment:
8
9```sh
10export DB_PATH="path/to/database"
11``` \ No newline at end of file
diff --git a/db/scripts/init.sh b/db/scripts/init.sh
index 1a8bbde..3baec36 100644
--- a/db/scripts/init.sh
+++ b/db/scripts/init.sh
@@ -1,10 +1,8 @@
1PROJECT_DIR=$(pwd) 1PROJECT_DIR=$(pwd)
2 2
3DB_PATH="$PROJECT_DIR/db/test.sqlite3" 3DB_PATH="$PROJECT_DIR/test.sqlite3"
4
5SQL_DIR="$PROJECT_DIR/db/sql"
6
7 4
5SQL_DIR="$PROJECT_DIR/sql"
8 6
9insert_user() { 7insert_user() {
10 read -p "Enter a username: " username 8 read -p "Enter a username: " username
diff --git a/fe/index.html b/fe/index.html
index b6c5f0a..3b39d6f 100644
--- a/fe/index.html
+++ b/fe/index.html
@@ -4,7 +4,7 @@
4 <meta charset="UTF-8" /> 4 <meta charset="UTF-8" />
5 <link rel="icon" type="image/svg+xml" href="/vite.svg" /> 5 <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7 <title>Vite + Svelte + TS</title> 7 <title>Track Your Water</title>
8 </head> 8 </head>
9 <body> 9 <body>
10 <div id="app"></div> 10 <div id="app"></div>
diff --git a/fe/src/utils.ts b/fe/src/utils.ts
index 9fddf41..41c733d 100644
--- a/fe/src/utils.ts
+++ b/fe/src/utils.ts
@@ -10,5 +10,5 @@ export function processFormInput(form: HTMLFormElement) {
10 10
11export function apiURL (path: string): string { 11export function apiURL (path: string): string {
12 const baseUrl = import.meta.env?.VITE_API_BASE_URL ?? "http://localhost:8080/api/v1"; 12 const baseUrl = import.meta.env?.VITE_API_BASE_URL ?? "http://localhost:8080/api/v1";
13 return `${baseUrl}${path}` 13 return `${baseUrl}/${path}`
14} 14}
diff --git a/fe/water.nginx.conf b/fe/water.nginx.conf
new file mode 100644
index 0000000..a715845
--- /dev/null
+++ b/fe/water.nginx.conf
@@ -0,0 +1,12 @@
1server {
2 root /srv/www/water;
3 index index.html index.htm;
4
5 listen 443 ssl;
6 listen [::]:443 ssl;
7 server_name water.example.com;
8
9 location / {
10 try_files $uri $uri/ =404;
11 }
12}