feat: switch to sqlite
This commit is contained in:
parent
78aa85dc20
commit
5b11552400
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -24,3 +24,4 @@ go.work
|
|||
gin-bin
|
||||
TODO
|
||||
.env
|
||||
*.db
|
||||
|
|
7
go.mod
7
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
|
||||
|
|
14
go.sum
14
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=
|
||||
|
|
102
main.go
102
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})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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 }} {{ .TotalCount }} {{ end }}</button>
|
||||
{{ end }}
|
||||
<button name="emojiInput" value="👍" type="submit" class="emoji-button">👍 {{ if gt (index .results "👍") 0 }} {{ index .results "👍"}} {{ end }}</button>
|
||||
<button name="emojiInput" value="👎" type="submit" class="emoji-button">👎 {{ if gt (index .results "👎") 0 }} {{ index .results "👎"}} {{ end }}</button>
|
||||
<button name="emojiInput" value="😀" type="submit" class="emoji-button">😀 {{ if gt (index .results "😀") 0 }} {{ index .results "😀"}} {{ end }}</button>
|
||||
<button name="emojiInput" value="😑" type="submit" class="emoji-button">😑 {{ if gt (index .results "😑") 0 }} {{ index .results "😑"}} {{ end }}</button>
|
||||
<button name="emojiInput" value="🤢" type="submit" class="emoji-button">🤢 {{ if gt (index .results "🤢") 0 }} {{ index .results "🤢"}} {{ end }}</button>
|
||||
<button name="emojiInput" value="👀" type="submit" class="emoji-button">👀 {{ if gt (index .results "👀") 0 }} {{ index .results "👀"}} {{ end }}</button>
|
||||
</div>
|
||||
</form>
|
||||
<p>{{.error}}</p>
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user