package main import ( "database/sql" "errors" "fmt" "log" "net/http" "os" "github.com/go-sql-driver/mysql" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" ) var db *sql.DB // Emoji counts for each post are retrieved in this format type EmojiCount struct { Emoji string TotalCount int } // Emojis' order should be like this var emojis = [6]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, } // 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 } // 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) } return emojiList, nil } func main() { mysqlHost := os.Getenv("DBHOST") cfg := mysql.Config{ User: os.Getenv("DBUSER"), Passwd: os.Getenv("DBPASS"), Net: "tcp", Addr: mysqlHost + ":3306", DBName: "emojis", } // Get a database handle. var err error db, err = sql.Open("mysql", cfg.FormatDSN()) if err != nil { log.Fatal(err) } // Check db connection pingErr := db.Ping() if pingErr != nil { log.Fatal(pingErr) } fmt.Println("Connected!") r := gin.Default() r.LoadHTMLGlob("templates/*") // CORS configuration corsConfig := cors.DefaultConfig() corsConfig.AllowOrigins = []string{"*"} corsConfig.AllowHeaders = []string{"hx-current-url", "hx-request"} r.Use(cors.New(corsConfig)) blogForm := r.Group("/blog/api/") { // Get the emoji form, you must provide postId query parameter blogForm.GET("/forms/emoji", func(c *gin.Context) { postId := c.Query("postId") // get emoji counts for each emoji emojiCounter, err := countEmojis(postId) if err != nil { c.HTML(http.StatusOK, "emoji_form.tmpl", gin.H{"error": "error getting the emoji counts", "postId": postId}) return } // Return the html template c.HTML(http.StatusOK, "emoji_form.tmpl", gin.H{ "postId": c.Query("postId"), "results": emojiCounter, }) }) // Update the emoji counter, this handler will // 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") // Check if parameters are missing if postId == "" || emoji == "" { c.HTML(http.StatusOK, "emoji_form.tmpl", gin.H{"error": "missing parameters", "postId": postId}) return } // Add the new emoji entry to the database _, err := db.Exec("INSERT INTO emoji_clicks (post_id, emoji) VALUES (?, ?)", postId, emoji) if err != nil { c.HTML(http.StatusOK, "emoji_form.tmpl", gin.H{"error": "error writing to database", "postId": postId}) return } // get emoji counts for each emoji emojiCounter, err := countEmojis(postId) if err != nil { c.HTML(http.StatusOK, "emoji_form.tmpl", gin.H{"error": "error getting the emoji counts", "postId": postId}) return } // Return the html with the updated emoji counter c.HTML(http.StatusOK, "emoji_form.tmpl", gin.H{"postId": postId, "results": emojiCounter}) }) } r.Run(":8000") }