aboutsummaryrefslogtreecommitdiff
path: root/api/internal
diff options
context:
space:
mode:
Diffstat (limited to 'api/internal')
-rw-r--r--api/internal/controllers/auth.go66
-rw-r--r--api/internal/controllers/stats.go164
-rw-r--r--api/internal/controllers/user.go17
-rw-r--r--api/internal/database/database.go22
-rw-r--r--api/internal/models/auth.go11
-rw-r--r--api/internal/models/preferences.go14
-rw-r--r--api/internal/models/statistics.go26
-rw-r--r--api/internal/models/user.go10
8 files changed, 330 insertions, 0 deletions
diff --git a/api/internal/controllers/auth.go b/api/internal/controllers/auth.go
new file mode 100644
index 0000000..744a884
--- /dev/null
+++ b/api/internal/controllers/auth.go
@@ -0,0 +1,66 @@
1package controllers
2
3import (
4 "encoding/base64"
5 "net/http"
6 "github.com/gin-gonic/gin"
7 "water/api/database"
8 "errors"
9 "crypto/rand"
10 "database/sql"
11
12 "water/api/models"
13 _ "github.com/mattn/go-sqlite3"
14 "golang.org/x/crypto/bcrypt"
15)
16
17func AuthHandler (c *gin.Context) {
18 username, password, ok := c.Request.BasicAuth()
19 if !ok {
20 c.Header("WWW-Authenticate", `Basic realm="Please enter your username and password."`)
21 c.AbortWithStatus(http.StatusUnauthorized)
22 return
23 }
24
25 db := database.EstablishDBConnection()
26 defer func(db *sql.DB) {
27 err := db.Close()
28 if err != nil {
29 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
30 return
31 }
32 }(db)
33
34 var user models.User
35 var preference models.Preference
36 var size models.Size
37
38 row := db.QueryRow("SELECT name, uuid, password, color, size, unit FROM Users u INNER JOIN Preferences p ON p.user_id = u.id INNER JOIN Sizes s ON p.size_id = s.id WHERE u.name = ?", username)
39 if err := row.Scan(&user.Name, &user.UUID, &user.Password, &preference.Color, &size.Size, &size.Unit); err != nil {
40 if errors.Is(err, sql.ErrNoRows) {
41 c.AbortWithStatus(http.StatusUnauthorized)
42 return
43 }
44 }
45
46 if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
47 c.AbortWithStatus(http.StatusUnauthorized)
48 return
49 }
50
51 preference.Size = size
52
53 // Generate a simple API token
54 apiToken := generateToken()
55 c.JSON(http.StatusOK, gin.H{"token": apiToken, "user": user, "preferences": preference})
56}
57
58// generatToken will g
59func generateToken() string {
60 token := make([]byte, 32)
61 _, err := rand.Read(token)
62 if err != nil {
63 return ""
64 }
65 return base64.StdEncoding.EncodeToString(token)
66} \ No newline at end of file
diff --git a/api/internal/controllers/stats.go b/api/internal/controllers/stats.go
new file mode 100644
index 0000000..9808ace
--- /dev/null
+++ b/api/internal/controllers/stats.go
@@ -0,0 +1,164 @@
1package controllers
2
3import (
4 "database/sql"
5 "github.com/gin-gonic/gin"
6 "net/http"
7 "water/api/database"
8 "water/api/models"
9)
10
11func GetAllStatistics(c *gin.Context) {
12 db := database.EstablishDBConnection()
13 defer func(db *sql.DB) {
14 err := db.Close()
15 if err != nil {
16 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
17 return
18 }
19 }(db)
20
21 rows, err := db.Query("SELECT s.date, s.quantity, u.uuid, u.name FROM Statistics s INNER JOIN Users u ON u.id = s.user_id")
22 if err != nil {
23 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
24 return
25 }
26 defer func(rows *sql.Rows) {
27 err := rows.Close()
28 if err != nil {
29 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
30 return
31 }
32 }(rows)
33
34 var data []models.Statistic
35
36 for rows.Next() {
37 var stat models.Statistic
38 var user models.User
39 if err := rows.Scan(&stat.Date, &stat.Quantity, &user.UUID, &user.Name); err != nil {
40 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
41 return
42 }
43 stat.User = user
44 data = append(data, stat)
45 }
46
47 c.JSON(http.StatusOK, data)
48}
49
50func PostNewStatistic(c *gin.Context) {
51 var stat models.StatisticPost
52
53 if err := c.BindJSON(&stat); err != nil {
54 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
55 return
56 }
57
58 db := database.EstablishDBConnection()
59 defer func(db *sql.DB) {
60 err := db.Close()
61 if err != nil {
62 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
63 return
64 }
65 }(db)
66
67 result, err := db.Exec("INSERT INTO statistics (date, user_id, quantity) values (?, ?, ?)", stat.Date, stat.UserID, stat.Quantity)
68
69 if err != nil {
70 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
71 }
72
73 id, err := result.LastInsertId()
74 if err != nil {
75 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
76 }
77
78 c.JSON(http.StatusCreated, gin.H{"status": "created", "id": id})
79}
80
81func GetWeeklyStatistics (c *gin.Context) {
82 db := database.EstablishDBConnection()
83 defer func(db *sql.DB) {
84 err := db.Close()
85 if err != nil {
86 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
87 return
88 }
89 }(db)
90
91 rows, err := db.Query("SELECT date, total FROM `WeeklyStatisticsView`")
92 if err != nil {
93 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
94 return
95 }
96 defer func(rows *sql.Rows) {
97 err := rows.Close()
98 if err != nil {
99 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
100 return
101 }
102 }(rows)
103
104 var data []models.WeeklyStatistic
105 for rows.Next() {
106 var weeklyStat models.WeeklyStatistic
107 if err := rows.Scan(&weeklyStat.Date, &weeklyStat.Total); err != nil {
108 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
109 }
110 data = append(data, weeklyStat)
111 }
112
113 c.JSON(http.StatusOK, data)
114}
115
116func GetDailyUserStatistics(c *gin.Context) {
117 db := database.EstablishDBConnection()
118 defer func(db *sql.DB) {
119 err := db.Close()
120 if err != nil {
121 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
122 return
123 }
124 }(db)
125
126 rows, err := db.Query("SELECT name, total FROM DailyUserStatistics")
127
128 if err != nil {
129 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
130 return
131 }
132 defer func(rows *sql.Rows) {
133 err := rows.Close()
134 if err != nil {
135 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
136 return
137 }
138 }(rows)
139
140 var data []models.DailyUserTotals
141 for rows.Next() {
142 var stat models.DailyUserTotals
143 if err := rows.Scan(&stat.Name, &stat.Total); err != nil {
144 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
145 return
146 }
147 data = append(data, stat)
148 }
149
150 c.JSON(http.StatusOK, data)
151
152}
153
154func GetUserStatistics(c *gin.Context) {
155 c.JSON(http.StatusOK, gin.H{"status": "ok", "uuid": c.Param("uuid")})
156}
157
158func UpdateUserStatistic(c *gin.Context) {
159 c.JSON(http.StatusNoContent, gin.H{"status": "No Content"})
160}
161
162func DeleteUserStatistic(c *gin.Context) {
163 c.JSON(http.StatusNoContent, gin.H{"status": "No Content"})
164}
diff --git a/api/internal/controllers/user.go b/api/internal/controllers/user.go
new file mode 100644
index 0000000..1f3f813
--- /dev/null
+++ b/api/internal/controllers/user.go
@@ -0,0 +1,17 @@
1package controllers
2
3import (
4 "net/http"
5 "github.com/gin-gonic/gin"
6)
7
8func GetUser(c *gin.Context) {
9 c.JSON(http.StatusOK, gin.H{"message": "User found"})
10}
11func GetUserPreferences(c *gin.Context) {
12 c.JSON(http.StatusOK, gin.H{"message": "Preferences fetched successfully"})
13}
14
15func UpdateUserPreferences(c *gin.Context) {
16 c.JSON(http.StatusOK, gin.H{"message": "Preferences updated successfully"})
17} \ No newline at end of file
diff --git a/api/internal/database/database.go b/api/internal/database/database.go
new file mode 100644
index 0000000..e313af5
--- /dev/null
+++ b/api/internal/database/database.go
@@ -0,0 +1,22 @@
1package database
2
3import (
4 "database/sql"
5 _ "github.com/mattn/go-sqlite3"
6 "log"
7)
8
9func SetupDatabase() {
10 _, err := sql.Open("sqlite3", "water.db")
11 if err != nil {
12 log.Fatal(err)
13 }
14}
15
16func EstablishDBConnection() *sql.DB {
17 db, err := sql.Open("sqlite3", "../db/water.sqlite3")
18 if err != nil {
19 panic(err)
20 }
21 return db
22} \ No newline at end of file
diff --git a/api/internal/models/auth.go b/api/internal/models/auth.go
new file mode 100644
index 0000000..41344d5
--- /dev/null
+++ b/api/internal/models/auth.go
@@ -0,0 +1,11 @@
1package models
2
3import "time"
4
5type Token struct {
6 ID int64 `json:"-"`
7 UserID int64 `json:"user_id"`
8 Token string `json:"token"`
9 CreatedAt time.Time `json:"created_at"`
10 ExpiredAt time.Time `json:"expired_at"`
11}
diff --git a/api/internal/models/preferences.go b/api/internal/models/preferences.go
new file mode 100644
index 0000000..cbbd47c
--- /dev/null
+++ b/api/internal/models/preferences.go
@@ -0,0 +1,14 @@
1package models
2
3type Preference struct {
4 ID int64 `json:"-"`
5 Color string `json:"color"`
6 UserID int64 `json:"-"`
7 Size Size `json:"size"`
8}
9
10type Size struct {
11 ID int64 `json:"-"`
12 Size int64 `json:"size"`
13 Unit string `json:"unit"`
14}
diff --git a/api/internal/models/statistics.go b/api/internal/models/statistics.go
new file mode 100644
index 0000000..457e6a0
--- /dev/null
+++ b/api/internal/models/statistics.go
@@ -0,0 +1,26 @@
1package models
2
3import "time"
4
5type Statistic struct {
6 ID int64 `json:"-"`
7 Date time.Time `json:"date"`
8 User User `json:"user"`
9 Quantity int `json:"quantity"`
10}
11
12type StatisticPost struct {
13 Date time.Time `json:"date"`
14 Quantity int64 `json:"quantity"`
15 UserID int64 `json:"user_id"`
16}
17
18type WeeklyStatistic struct {
19 Date string `json:"date"`
20 Total int64 `json:"total"`
21}
22
23type DailyUserTotals struct {
24 Name string `json:"name"`
25 Total int64 `json:"total"`
26}
diff --git a/api/internal/models/user.go b/api/internal/models/user.go
new file mode 100644
index 0000000..2a3e6fd
--- /dev/null
+++ b/api/internal/models/user.go
@@ -0,0 +1,10 @@
1package models
2
3import "github.com/google/uuid"
4
5type User struct {
6 ID int64 `json:"-"`
7 Name string `json:"name"`
8 UUID uuid.UUID `json:"uuid"`
9 Password string `json:"-"`
10}