package main import ( "net/http" "crypto/rand" "encoding/base64" "database/sql" "strings" "errors" "log" "github.com/gin-gonic/gin" _ "github.com/mattn/go-sqlite3" "golang.org/x/crypto/bcrypt" "water/api/lib" ) func CORSMiddleware() gin.HandlerFunc { return func(c *gin.Context) { c.Writer.Header().Set("Access-Control-Allow-Origin", "*") c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") 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") c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT") log.Println("I am here") if c.Request.Method == "OPTIONS" { log.Println(c.Request.Header) c.AbortWithStatus(204) return } log.Println(c.Request.Header) c.Next() } } // generatToken will g func generateToken() string { token := make([]byte, 32) rand.Read(token) 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() } } func setupRouter() *gin.Engine { // Disable Console Color // gin.DisableConsoleColor() r := gin.Default() r.Use(CORSMiddleware()) r.Use(gin.Logger()) r.Use(gin.Recovery()) api := r.Group("api/v1") api.POST("/auth", func(c *gin.Context) { username, password, ok := c.Request.BasicAuth() if !ok { c.Header("WWW-Authenticate", `Basic realm="Please enter your username and password."`) c.AbortWithStatus(http.StatusUnauthorized) return } db := establishDBConnection() defer db.Close() var user models.User var preference models.Preference var size models.Size 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) if err := row.Scan(&user.Name, &user.UUID, &user.Password, &preference.Color, &size.Size, &size.Unit); err != nil { if err == sql.ErrNoRows { c.AbortWithStatus(http.StatusUnauthorized) return } } if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil { c.AbortWithStatus(http.StatusUnauthorized) return } preference.Size = size // Generate a simple API token apiToken := generateToken() c.JSON(http.StatusOK, gin.H{"token": apiToken, "user": user, "preferences": preference}) }) stats := api.Group("/stats") stats.Use(TokenRequired()) { stats.GET("/", func(c *gin.Context) { db := establishDBConnection() defer db.Close() 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") 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 var user models.User if err := rows.Scan(&stat.Date, &stat.Quantity, &user.UUID, &user.Name); err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } stat.User = user data = append(data, stat) } // TODO: return to this and figure out how to best collect the data you are looking for for each user (zach and parker) rows, err = db.Query("SELECT date(s.date), SUM(s.quantity) as total FROM Statistics s WHERE s.date >= date('now', '-7 days') GROUP BY DATE(s.date)") if err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } defer rows.Close() var dailySummaries []models.DailySummary for rows.Next() { var summary models.DailySummary if err := rows.Scan(&summary.Date, &summary.Total); err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } dailySummaries = append(dailySummaries, summary) } c.JSON(http.StatusOK, gin.H{"stats": data, "totals": dailySummaries}) rows, err = db.Query("SELECT s.date, SUM(s.quantity) as total, u.uuid, u.name FROM Statistics s INNER JOIN Users u ON u.id = s.user_id WHERE s.date >= date('now', '-7 days') GROUP BY s.date, s.user_id") if err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } defer rows.Close() var totals []interface{} for rows.Next() { var stat models.Statistic var user models.User if err := rows.Scan(&stat.Date, &stat.Quantity, &user.UUID, &user.Name); err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } stat.User = user totals = append(totals, stat) } c.JSON(http.StatusOK, gin.H{"stats": data, "totals": totals}) }) 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, 1, 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("/totals/", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"status": "ok"}) }) // stats.GET("/totals/", func(c *gin.Context) { // db := establishDBConnection() // defer db.Close() // // rows, err := db.Query("SELECT s.date, SUM(s.quantity) as total, u.uuid, u.name FROM Statistics s INNER JOIN Users u ON u.id = s.user_id WHERE s.date >= date('now', '-7 days') GROUP BY s.date, s.user_id") // // 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 // var user models.User // if err := rows.Scan(&stat.Date, &stat.Quantity, &user.UUID, &user.Name); err != nil { // c.JSON(500, gin.H{"error": err.Error()}) // return // } // stat.User = user // data = append(data, stat) // } // // c.JSON(http.StatusOK, data) // // }) stats.GET("user/:uuid", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"status": "ok", "uuid": c.Param("uuid")}) }) stats.PATCH("user/:uuid", func(c *gin.Context) { c.JSON(http.StatusNoContent, gin.H{"status": "No Content"}) }) stats.DELETE("user/:uuid", func(c *gin.Context) { c.JSON(http.StatusNoContent, gin.H{"status": "No Content"}) }) } return r } func main() { r := setupRouter() // Listen and Server in 0.0.0.0:8080 r.Run(":8080") }