aboutsummaryrefslogtreecommitdiff
path: root/api/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'api/main.go')
-rw-r--r--api/main.go301
1 files changed, 0 insertions, 301 deletions
diff --git a/api/main.go b/api/main.go
deleted file mode 100644
index 17a3c3a..0000000
--- a/api/main.go
+++ /dev/null
@@ -1,301 +0,0 @@
1package main
2
3import (
4 "crypto/rand"
5 "database/sql"
6 "encoding/base64"
7 "errors"
8 "log"
9 "net/http"
10 "strings"
11
12 "github.com/gin-gonic/gin"
13 _ "github.com/mattn/go-sqlite3"
14 "golang.org/x/crypto/bcrypt"
15)
16
17func CORSMiddleware() gin.HandlerFunc {
18 return func(c *gin.Context) {
19 c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
20 c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
21 c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
22 c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
23
24 if c.Request.Method == "OPTIONS" {
25 log.Println(c.Request.Header)
26 c.AbortWithStatus(http.StatusNoContent)
27 return
28 }
29
30 c.Next()
31 }
32}
33
34// generatToken will g
35func generateToken() string {
36 token := make([]byte, 32)
37 _, err := rand.Read(token)
38 if err != nil {
39 return ""
40 }
41 return base64.StdEncoding.EncodeToString(token)
42}
43
44func establishDBConnection() *sql.DB {
45 db, err := sql.Open("sqlite3", "../db/water.sqlite3")
46 if err != nil {
47 panic(err)
48 }
49 return db
50}
51
52func checkForTokenInContext(c *gin.Context) (string, error) {
53 authorizationHeader := c.GetHeader("Authorization")
54 if authorizationHeader == "" {
55 return "", errors.New("authorization header is missing")
56 }
57
58 parts := strings.Split(authorizationHeader, " ")
59
60 if len(parts) != 2 || parts[0] != "Bearer" {
61 return "", errors.New("invalid Authorization header format")
62 }
63
64 return parts[1], nil
65}
66
67func TokenRequired() gin.HandlerFunc {
68 return func(c *gin.Context) {
69 _, err := checkForTokenInContext(c)
70
71 if err != nil {
72 c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
73 c.Abort()
74 return
75 }
76
77 c.Next()
78 }
79}
80
81func setupRouter() *gin.Engine {
82 // Disable Console Color
83 // gin.DisableConsoleColor()
84 r := gin.Default()
85 r.Use(CORSMiddleware())
86 r.Use(gin.Logger())
87 r.Use(gin.Recovery())
88
89 api := r.Group("api/v1")
90
91 api.POST("/auth", func(c *gin.Context) {
92 username, password, ok := c.Request.BasicAuth()
93 if !ok {
94 c.Header("WWW-Authenticate", `Basic realm="Please enter your username and password."`)
95 c.AbortWithStatus(http.StatusUnauthorized)
96 return
97 }
98
99 db := establishDBConnection()
100 defer func(db *sql.DB) {
101 err := db.Close()
102 if err != nil {
103 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
104 return
105 }
106 }(db)
107
108 var user User
109 var preference Preference
110 var size Size
111
112 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)
113 if err := row.Scan(&user.Name, &user.UUID, &user.Password, &preference.Color, &size.Size, &size.Unit); err != nil {
114 if errors.Is(err, sql.ErrNoRows) {
115 c.AbortWithStatus(http.StatusUnauthorized)
116 return
117 }
118 }
119
120 if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
121 c.AbortWithStatus(http.StatusUnauthorized)
122 return
123 }
124
125 preference.Size = size
126
127 // Generate a simple API token
128 apiToken := generateToken()
129 c.JSON(http.StatusOK, gin.H{"token": apiToken, "user": user, "preferences": preference})
130 })
131
132 stats := api.Group("/stats")
133 stats.Use(TokenRequired())
134 {
135 stats.GET("/", func(c *gin.Context) {
136 db := establishDBConnection()
137 defer func(db *sql.DB) {
138 err := db.Close()
139 if err != nil {
140 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
141 return
142 }
143 }(db)
144
145 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")
146 if err != nil {
147 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
148 return
149 }
150 defer func(rows *sql.Rows) {
151 err := rows.Close()
152 if err != nil {
153 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
154 return
155 }
156 }(rows)
157
158 var data []Statistic
159
160 for rows.Next() {
161 var stat Statistic
162 var user User
163 if err := rows.Scan(&stat.Date, &stat.Quantity, &user.UUID, &user.Name); err != nil {
164 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
165 return
166 }
167 stat.User = user
168 data = append(data, stat)
169 }
170
171 c.JSON(http.StatusOK, data)
172 })
173
174 stats.POST("/", func(c *gin.Context) {
175 var stat StatisticPost
176
177 if err := c.BindJSON(&stat); err != nil {
178 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
179 return
180 }
181
182 db := establishDBConnection()
183 defer func(db *sql.DB) {
184 err := db.Close()
185 if err != nil {
186 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
187 return
188 }
189 }(db)
190
191 result, err := db.Exec("INSERT INTO statistics (date, user_id, quantity) values (?, ?, ?)", stat.Date, stat.UserID, stat.Quantity)
192
193 if err != nil {
194 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
195 }
196
197 id, err := result.LastInsertId()
198 if err != nil {
199 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
200 }
201
202 c.JSON(http.StatusCreated, gin.H{"status": "created", "id": id})
203 })
204
205 stats.GET("weekly/", func(c *gin.Context) {
206 db := establishDBConnection()
207 defer func(db *sql.DB) {
208 err := db.Close()
209 if err != nil {
210 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
211 return
212 }
213 }(db)
214
215 rows, err := db.Query("SELECT date, total FROM `WeeklyStatisticsView`")
216 if err != nil {
217 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
218 return
219 }
220 defer func(rows *sql.Rows) {
221 err := rows.Close()
222 if err != nil {
223 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
224 return
225 }
226 }(rows)
227
228 var data []WeeklyStatistic
229 for rows.Next() {
230 var weeklyStat WeeklyStatistic
231 if err := rows.Scan(&weeklyStat.Date, &weeklyStat.Total); err != nil {
232 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
233 }
234 data = append(data, weeklyStat)
235 }
236
237 c.JSON(http.StatusOK, data)
238 })
239
240 stats.GET("totals/", func(c *gin.Context) {
241 db := establishDBConnection()
242 defer func(db *sql.DB) {
243 err := db.Close()
244 if err != nil {
245 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
246 return
247 }
248 }(db)
249
250 rows, err := db.Query("SELECT name, total FROM DailyUserStatistics")
251
252 if err != nil {
253 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
254 return
255 }
256 defer func(rows *sql.Rows) {
257 err := rows.Close()
258 if err != nil {
259 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
260 return
261 }
262 }(rows)
263
264 var data []DailyUserTotals
265 for rows.Next() {
266 var stat DailyUserTotals
267 if err := rows.Scan(&stat.Name, &stat.Total); err != nil {
268 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
269 return
270 }
271 data = append(data, stat)
272 }
273
274 c.JSON(http.StatusOK, data)
275
276 })
277
278 stats.GET("user/:uuid", func(c *gin.Context) {
279 c.JSON(http.StatusOK, gin.H{"status": "ok", "uuid": c.Param("uuid")})
280 })
281
282 stats.PATCH("user/:uuid", func(c *gin.Context) {
283 c.JSON(http.StatusNoContent, gin.H{"status": "No Content"})
284 })
285
286 stats.DELETE("user/:uuid", func(c *gin.Context) {
287 c.JSON(http.StatusNoContent, gin.H{"status": "No Content"})
288 })
289 }
290
291 return r
292}
293
294func main() {
295 r := setupRouter()
296 // Listen and Server in 0.0.0.0:8080
297 err := r.Run(":8080")
298 if err != nil {
299 return
300 }
301}