feat: switch to sqlite

This commit is contained in:
log101 2024-06-10 07:50:15 +03:00
parent 78aa85dc20
commit 5b11552400
5 changed files with 57 additions and 80 deletions

1
.gitignore vendored
View File

@ -24,3 +24,4 @@ go.work
gin-bin
TODO
.env
*.db

7
go.mod
View File

@ -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

14
go.sum
View File

@ -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=

102
main.go
View File

@ -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})
})
}

View File

@ -1,10 +1,9 @@
<form hx-post={{ .hxPostUrl }} hx-swap="outerHTML">
<input type="hidden" name="postId" value="{{.postId}}">
<div class="emoji-buttons-container">
{{ range .results }}
<button name="emojiInput" value={{ .Emoji }} type="submit" class="emoji-button">{{ .Emoji }} {{ if gt .TotalCount 0 }} &nbsp; {{ .TotalCount }} {{ end }}</button>
{{ end }}
<button name="emojiInput" value="👍" type="submit" class="emoji-button">👍&nbsp; {{ if gt (index .results "👍") 0 }} {{ index .results "👍"}} {{ end }}</button>
<button name="emojiInput" value="👎" type="submit" class="emoji-button">👎&nbsp; {{ if gt (index .results "👎") 0 }} {{ index .results "👎"}} {{ end }}</button>
<button name="emojiInput" value="😀" type="submit" class="emoji-button">😀&nbsp; {{ if gt (index .results "😀") 0 }} {{ index .results "😀"}} {{ end }}</button>
<button name="emojiInput" value="😑" type="submit" class="emoji-button">😑&nbsp; {{ if gt (index .results "😑") 0 }} {{ index .results "😑"}} {{ end }}</button>
<button name="emojiInput" value="🤢" type="submit" class="emoji-button">🤢&nbsp; {{ if gt (index .results "🤢") 0 }} {{ index .results "🤢"}} {{ end }}</button>
<button name="emojiInput" value="👀" type="submit" class="emoji-button">👀&nbsp; {{ if gt (index .results "👀") 0 }} {{ index .results "👀"}} {{ end }}</button>
</div>
</form>
<p>{{.error}}</p>