diff --git a/.gitignore b/.gitignore index d98725e..6cf2d32 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ go.work gin-bin TODO .env +*.db diff --git a/go.mod b/go.mod index 7ddb76e..4192448 100644 --- a/go.mod +++ b/go.mod @@ -5,12 +5,12 @@ go 1.22.3 require ( github.com/gin-contrib/cors v1.7.2 github.com/gin-gonic/gin v1.10.0 - github.com/go-sql-driver/mysql v1.8.1 github.com/joho/godotenv v1.5.1 + gorm.io/driver/sqlite v1.5.5 + gorm.io/gorm v1.25.10 ) require ( - filippo.io/edwards25519 v1.1.0 // indirect github.com/bytedance/sonic v1.11.7 // indirect github.com/bytedance/sonic/loader v0.1.1 // indirect github.com/cloudwego/base64x v0.1.4 // indirect @@ -21,11 +21,14 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.20.0 // indirect github.com/goccy/go-json v0.10.3 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect diff --git a/go.sum b/go.sum index 0769675..61724f4 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= -filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/bytedance/sonic v1.11.7 h1:k/l9p1hZpNIMJSk37wL9ltkcpqLfIho1vYthi4xT2t4= github.com/bytedance/sonic v1.11.7/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= @@ -28,13 +26,15 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= -github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -51,6 +51,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -101,5 +103,9 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E= +gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE= +gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= +gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/main.go b/main.go index 505c3b6..d6c9a06 100644 --- a/main.go +++ b/main.go @@ -1,70 +1,51 @@ package main import ( - "database/sql" "errors" - "fmt" "log" "net/http" - "os" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" - "github.com/go-sql-driver/mysql" "github.com/joho/godotenv" + "gorm.io/driver/sqlite" + "gorm.io/gorm" "log101-blog-services/middleware" ) -var db *sql.DB +var db *gorm.DB // Emoji counts for each post are retrieved in this format -type EmojiCount struct { +type PostReaction struct { Emoji string TotalCount int } -// Emojis' order should be like this -var emojis = [6]string{"👍", "👎", "😀", "😑", "🤢", "👀"} +type EmojiReaction struct { + UserAnonIp string + PostId string + Emoji string +} // Get the emoji counts foe a given post id -func countEmojis(postId string) ([]EmojiCount, error) { - emojiList := []EmojiCount{} - // This will hold the total counts for each emoji - // for a given post - var emojiCounter = map[string]int{ - "👍": 0, - "👎": 0, - "😀": 0, - "😑": 0, - "🤢": 0, - "👀": 0, - } +func countEmojis(postId string) (map[string]int, error) { + postReactions := []PostReaction{} // Get the emoji counts for each emoji - rows, err := db.Query("SELECT emoji, COUNT(*) FROM emoji_clicks WHERE post_id = ? GROUP BY emoji;", postId) - if err != nil { - return emojiList, errors.New("couldn't get emoji counts") - } - defer rows.Close() - - // Read the rows and update the counter - for rows.Next() { - var ec EmojiCount - if err := rows.Scan(&ec.Emoji, &ec.TotalCount); err != nil { - return emojiList, errors.New("DB results couldn't read") - } - emojiCounter[ec.Emoji] = ec.TotalCount + // rows, err := db.Query("SELECT emoji, COUNT(*) FROM emoji_clicks WHERE post_id = ? GROUP BY emoji;", postId) + rows := db.Model(&EmojiReaction{}).Where("post_id = ?", postId).Select([]string{"emoji", "COUNT(*) as total_count"}).Group("emoji").Find(&postReactions) + if rows.Error != nil { + return nil, errors.New("couldn't get emoji counts") } - // Emojis are returned as an ordered list - // as maps are randomly iterated - for _, emoji := range emojis { - var count = EmojiCount{emoji, emojiCounter[emoji]} - emojiList = append(emojiList, count) + // Transform rows to map + result := make(map[string]int) + for _, reaction := range postReactions { + result[reaction.Emoji] = reaction.TotalCount } - return emojiList, nil + return result, nil } func main() { @@ -74,26 +55,12 @@ func main() { log.Fatal("Error loading .env file") } - mysqlHost := os.Getenv("DBHOST") - cfg := mysql.Config{ - User: os.Getenv("DBUSER"), - Passwd: os.Getenv("DBPASS"), - Net: "tcp", - Addr: mysqlHost + ":3306", - DBName: "emojis", - } - - db, err = sql.Open("mysql", cfg.FormatDSN()) + db, err = gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) if err != nil { - log.Fatal(err) + panic("failed to connect database") } - // Check db connection - pingErr := db.Ping() - if pingErr != nil { - log.Fatal(pingErr) - } - fmt.Println("Connected!") + db.AutoMigrate(&EmojiReaction{}) r := gin.Default() r.LoadHTMLGlob("templates/*") @@ -107,7 +74,7 @@ func main() { corsConfig.AllowOrigins = append(corsConfig.AllowOrigins, "http://localhost:4321") } - corsConfig.AllowHeaders = []string{"hx-current-url", "hx-request"} + corsConfig.AllowHeaders = []string{"hx-current-url", "hx-request", "hx-target", "hx-trigger"} // Middlewares r.Use(cors.New(corsConfig)) @@ -145,31 +112,32 @@ func main() { // add a new entry to the database with anonymized ip // then sums the results to get the emoji counter blogForm.POST("/forms/emoji/post", func(c *gin.Context) { - postId := c.PostForm("postId") - emoji := c.PostForm("emojiInput") + reactedPostId := c.PostForm("postId") + reaction := c.PostForm("emojiInput") // Check if parameters are missing - if postId == "" || emoji == "" { - c.HTML(http.StatusOK, "emoji_form.tmpl", gin.H{"error": "missing parameters", "postId": postId, "hxPostUrl": hxPostUrl}) + if reactedPostId == "" || reaction == "" { + c.HTML(http.StatusOK, "emoji_form.tmpl", gin.H{"error": "missing parameters", "postId": reactedPostId, "hxPostUrl": hxPostUrl}) return } // Add the new emoji entry to the database - _, err := db.Exec("INSERT INTO emoji_clicks (user_anon_data, post_id, emoji) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE emoji = ?", c.Request.RemoteAddr, postId, emoji, emoji) - if err != nil { - c.HTML(http.StatusOK, "emoji_form.tmpl", gin.H{"error": "error writing to database", "postId": postId, "hxPostUrl": hxPostUrl}) + // _, err := db.Exec("INSERT INTO emoji_clicks (user_anon_data, post_id, emoji) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE emoji = ?", c.Request.RemoteAddr, postId, emoji, emoji) + result := db.Create(&EmojiReaction{UserAnonIp: c.Request.RemoteAddr, Emoji: reaction, PostId: reactedPostId}) + if result.Error != nil { + c.HTML(http.StatusOK, "emoji_form.tmpl", gin.H{"error": "error writing to database", "postId": reactedPostId, "hxPostUrl": hxPostUrl}) return } // get emoji counts for each emoji - emojiCounter, err := countEmojis(postId) + emojiCounter, err := countEmojis(reactedPostId) if err != nil { - c.HTML(http.StatusOK, "emoji_form.tmpl", gin.H{"error": "error getting the emoji counts", "postId": postId, "hxPostUrl": hxPostUrl}) + c.HTML(http.StatusOK, "emoji_form.tmpl", gin.H{"error": "error getting the emoji counts", "postId": reactedPostId, "hxPostUrl": hxPostUrl}) return } // Return the html with the updated emoji counter - c.HTML(http.StatusOK, "emoji_form.tmpl", gin.H{"postId": postId, "results": emojiCounter, "hxPostUrl": hxPostUrl}) + c.HTML(http.StatusOK, "emoji_form.tmpl", gin.H{"postId": reactedPostId, "results": emojiCounter, "hxPostUrl": hxPostUrl}) }) } diff --git a/templates/emoji_form.tmpl b/templates/emoji_form.tmpl index fb86881..32672e2 100644 --- a/templates/emoji_form.tmpl +++ b/templates/emoji_form.tmpl @@ -1,10 +1,9 @@ -
-{{.error}}