diff options
author | Zach Berwaldt <zberwaldt@tutamail.com> | 2024-03-18 21:27:24 -0400 |
---|---|---|
committer | zberwaldt <17715430+zberwaldt@users.noreply.github.com> | 2024-03-18 21:30:13 -0400 |
commit | 53b74ec6122c94853f9d43ab2ac1f69efab0886d (patch) | |
tree | 02b6d887d5d0ba5d887ef0334f9dba07d3814292 | |
parent | e5fbe360d355557b978973f62eed06e01e85bb71 (diff) |
clean up, add better error handling
-rwxr-xr-x | api/bin/water | bin | 0 -> 15831520 bytes | |||
-rw-r--r-- | api/internal/controllers/auth.go | 21 | ||||
-rw-r--r-- | api/internal/controllers/preferences.go | 13 | ||||
-rw-r--r-- | api/internal/controllers/stats.go | 27 | ||||
-rw-r--r-- | api/internal/controllers/user.go | 14 | ||||
-rw-r--r-- | api/internal/database/database.go | 18 | ||||
-rwxr-xr-x | api/main | bin | 0 -> 17796784 bytes | |||
-rw-r--r-- | api/water-api.nginx.conf | 8 | ||||
-rw-r--r-- | api/water.service | 14 | ||||
-rw-r--r-- | db/README.md | 10 | ||||
-rw-r--r-- | db/scripts/init.sh | 6 | ||||
-rw-r--r-- | fe/index.html | 2 | ||||
-rw-r--r-- | fe/src/utils.ts | 2 | ||||
-rw-r--r-- | fe/water.nginx.conf | 12 |
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. |
22 | func AuthHandler (c *gin.Context) { | 22 | func 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 @@ | |||
1 | package controllers | 1 | package controllers |
2 | 2 | ||
3 | import ( | 3 | import ( |
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 | ||
11 | func GetSizes(c *gin.Context) { | 12 | func 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 | ||
3 | import ( | 3 | import ( |
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. |
15 | func GetAllStatistics(c *gin.Context) { | 16 | func 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 | ||
85 | func GetWeeklyStatistics(c *gin.Context) { | 94 | func 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 | ||
120 | func GetDailyUserStatistics(c *gin.Context) { | 133 | func 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 | } |
17 | func GetUserPreferences(c *gin.Context) { | 17 | func 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 | ||
43 | func UpdateUserPreferences(c *gin.Context) { | 47 | func 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 | ||
3 | import ( | 3 | import ( |
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 | ||
11 | func EstablishDBConnection() *sql.DB { | 13 | func 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 @@ | |||
1 | server { | ||
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] | ||
2 | Description=Water is a backend for tracking water consumption | ||
3 | StartLimitIntervalSec=600 | ||
4 | StartLimitBurst=2 | ||
5 | |||
6 | [Service] | ||
7 | WorkingDirectory=/path/to/water/binary | ||
8 | User=water | ||
9 | Restart=on-failure | ||
10 | RestartSec=30 | ||
11 | PermissionsStartOnly=true | ||
12 | |||
13 | [Install] | ||
14 | WantedBy=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 | ||
3 | This document describes how to set up the database for your instance of the water application. \ No newline at end of file | 3 | This document describes how to set up the database for your instance of the water application. |
4 | |||
5 | ## Setup | ||
6 | |||
7 | The 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 | ||
10 | export 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 @@ | |||
1 | PROJECT_DIR=$(pwd) | 1 | PROJECT_DIR=$(pwd) |
2 | 2 | ||
3 | DB_PATH="$PROJECT_DIR/db/test.sqlite3" | 3 | DB_PATH="$PROJECT_DIR/test.sqlite3" |
4 | |||
5 | SQL_DIR="$PROJECT_DIR/db/sql" | ||
6 | |||
7 | 4 | ||
5 | SQL_DIR="$PROJECT_DIR/sql" | ||
8 | 6 | ||
9 | insert_user() { | 7 | insert_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 | ||
11 | export function apiURL (path: string): string { | 11 | export 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 @@ | |||
1 | server { | ||
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 | } | ||