Enhance project functionality: add email template, implement email sending, and improve table management routes
The table management in the database does currently not work
This commit is contained in:
17
src/main.go
17
src/main.go
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-contrib/sessions/cookie"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/joho/godotenv"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -34,9 +35,17 @@ const (
|
||||
func main() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
var err error
|
||||
err = godotenv.Load()
|
||||
if err != nil {
|
||||
log.Fatal("Error loading .env file")
|
||||
}
|
||||
|
||||
// Get the listening port
|
||||
port := os.Getenv("APP_PORT")
|
||||
port = strconv.Itoa(defaultPort)
|
||||
if port == "" {
|
||||
port = strconv.Itoa(defaultPort)
|
||||
}
|
||||
|
||||
// Set the base URL
|
||||
baseURL = os.Getenv("APP_BASE_URL")
|
||||
@@ -45,7 +54,6 @@ func main() {
|
||||
}
|
||||
|
||||
// Open connection to SQLite
|
||||
var err error
|
||||
db, err = gorm.Open(sqlite.Open("qrank.db"), &gorm.Config{})
|
||||
if err != nil {
|
||||
lg.Fatal("sqlite connect:", err)
|
||||
@@ -84,8 +92,9 @@ func main() {
|
||||
authorized.POST("/enter", postEnter)
|
||||
|
||||
// QR-prepped table routes
|
||||
authorized.GET("/table/:tableSlug", getEnter)
|
||||
authorized.POST("/table/:tableSlug", postEnter)
|
||||
authorized.GET("/table/:tableSlug", getTable)
|
||||
authorized.POST("/table/:tableSlug", postTable)
|
||||
authorized.POST("/table/:tableSlug/reset", postTableReset)
|
||||
|
||||
authorized.GET("/history", getHistory)
|
||||
authorized.GET("/leaderboard", getLeaderboard)
|
||||
|
||||
@@ -67,5 +67,5 @@ type LoginToken struct {
|
||||
type Table struct {
|
||||
gorm.Model
|
||||
Name string
|
||||
Slug string
|
||||
Slug string `gorm:"uniqueIndex;size:128"`
|
||||
}
|
||||
|
||||
123
src/routes.go
123
src/routes.go
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -9,11 +10,30 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func getEnter(c *gin.Context) {
|
||||
sess := sessions.Default(c)
|
||||
tableSlug := sess.Get("TableSlug")
|
||||
|
||||
var slug string
|
||||
if tableSlug != nil {
|
||||
slug = tableSlug.(string)
|
||||
|
||||
}
|
||||
|
||||
if slug != "" {
|
||||
|
||||
var table *Table
|
||||
db.Model(&Table{}).Where("slug = ?", slug).First(&table)
|
||||
|
||||
tm.Render(c, "enter", gin.H{"Table": table})
|
||||
return
|
||||
|
||||
}
|
||||
tm.Render(c, "enter", gin.H{})
|
||||
}
|
||||
|
||||
@@ -24,6 +44,16 @@ func postEnter(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
sess := sessions.Default(c)
|
||||
tableSlug := sess.Get("TableSlug")
|
||||
|
||||
var slug string
|
||||
if tableSlug != nil {
|
||||
slug = tableSlug.(string)
|
||||
|
||||
}
|
||||
sess.Delete("TableSlug")
|
||||
|
||||
// Parse form
|
||||
a1h := strings.TrimSpace(c.PostForm("a1"))
|
||||
a2h := strings.TrimSpace(c.PostForm("a2"))
|
||||
@@ -96,9 +126,18 @@ func postEnter(c *gin.Context) {
|
||||
}
|
||||
|
||||
// Create game
|
||||
g := Game{
|
||||
ScoreA: scoreA,
|
||||
ScoreB: scoreB,
|
||||
var g Game
|
||||
if slug != "" {
|
||||
g = Game{
|
||||
ScoreA: scoreA,
|
||||
ScoreB: scoreB,
|
||||
Table: &Table{Slug: slug},
|
||||
}
|
||||
} else {
|
||||
g = Game{
|
||||
ScoreA: scoreA,
|
||||
ScoreB: scoreB,
|
||||
}
|
||||
}
|
||||
if err := db.Create(&g).Error; err != nil {
|
||||
c.String(http.StatusInternalServerError, "game create error")
|
||||
@@ -304,6 +343,10 @@ func postLogin(c *gin.Context) {
|
||||
link := strings.TrimRight(baseURL, "/") + "/magic?token=" + token
|
||||
log.Printf("[MAGIC LINK] %s for %s (valid until %s)\n", link, email, lt.ExpiresAt.Format(time.RFC3339))
|
||||
|
||||
if err := sendEmail(email, link); err != nil {
|
||||
c.Status(500)
|
||||
}
|
||||
|
||||
tm.Render(c, "sent", gin.H{"Email": email})
|
||||
}
|
||||
|
||||
@@ -335,3 +378,77 @@ func getMagic(c *gin.Context) {
|
||||
|
||||
c.Redirect(302, "/enter")
|
||||
}
|
||||
|
||||
type TableInfo struct {
|
||||
Players map[uint]*User
|
||||
PlayerCount int
|
||||
}
|
||||
|
||||
var tables = make(map[string]*TableInfo)
|
||||
|
||||
func getTable(c *gin.Context) {
|
||||
slug := c.Param("tableSlug")
|
||||
u := getSessionUser(c)
|
||||
if tables[slug] == nil {
|
||||
tables[slug] = &TableInfo{}
|
||||
}
|
||||
|
||||
table := tables[slug]
|
||||
if tables[slug].Players == nil {
|
||||
tables[slug].Players = make(map[uint]*User)
|
||||
}
|
||||
|
||||
if table.PlayerCount >= 4 {
|
||||
tm.Render(c, "table_status", gin.H{"Full": "true", "CurrentSlug": slug, "Table": tables[slug]})
|
||||
return
|
||||
}
|
||||
|
||||
table.Players[u.ID] = u
|
||||
table.PlayerCount = len(table.Players)
|
||||
|
||||
tm.Render(c, "table_status", gin.H{"CurrentSlug": slug, "Table": tables[slug]})
|
||||
}
|
||||
|
||||
func postTable(c *gin.Context) {
|
||||
slug := c.Param("tableSlug")
|
||||
table := tables[slug]
|
||||
session := sessions.Default(c)
|
||||
|
||||
if len(table.Players) == 2 || len(table.Players) == 4 {
|
||||
|
||||
// Pretend we got some values from somewhere else
|
||||
var count int
|
||||
tags := []string{"a1", "b1", "a2", "b2"}
|
||||
formData := make(map[string]string)
|
||||
for _, value := range table.Players {
|
||||
formData[tags[count]] = value.Username
|
||||
count++
|
||||
}
|
||||
if b, err := json.Marshal(formData); err == nil {
|
||||
session.Set("form_data", string(b))
|
||||
_ = session.Save()
|
||||
}
|
||||
|
||||
session.Set("TableSlug", slug)
|
||||
_ = session.Save()
|
||||
|
||||
// Reset the table
|
||||
tables[slug] = &TableInfo{}
|
||||
|
||||
db.Save(&Table{Slug: slug})
|
||||
|
||||
c.Redirect(302, "/enter")
|
||||
return
|
||||
|
||||
}
|
||||
SetMessage(c, "Wrong amount of players!")
|
||||
c.Redirect(302, "/table/"+slug)
|
||||
|
||||
}
|
||||
|
||||
func postTableReset(c *gin.Context) {
|
||||
slug := c.Param("tableSlug")
|
||||
tables[slug] = &TableInfo{}
|
||||
SetMessage(c, "Resettet the table!")
|
||||
c.Redirect(302, "/table/"+slug)
|
||||
}
|
||||
|
||||
44
src/utils.go
44
src/utils.go
@@ -1,11 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"html/template"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gopkg.in/gomail.v2"
|
||||
)
|
||||
|
||||
func mustRandToken(n int) string {
|
||||
@@ -134,3 +141,40 @@ func stripAfterDot(s string) string {
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func nameFromEmail(email string) string {
|
||||
parts := strings.SplitN(email, "@", 2)
|
||||
if len(parts) > 0 {
|
||||
return parts[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func sendEmail(email string, token string) error {
|
||||
// Parse the email body template
|
||||
tmpl, err := template.ParseFiles("other/email.txt")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Execute template with provided data
|
||||
var body bytes.Buffer
|
||||
if err := tmpl.Execute(&body, gin.H{"Token": token, "Name": nameFromEmail(email)}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Compose email
|
||||
m := gomail.NewMessage()
|
||||
m.SetHeader("From", os.Getenv("SMTP_MAIL"))
|
||||
m.SetHeader("To", email)
|
||||
m.SetHeader("Subject", "Registration/Login to QRank")
|
||||
m.SetBody("text/plain", body.String())
|
||||
|
||||
// Dial and send
|
||||
d := gomail.NewDialer(os.Getenv("SMTP_HOST"), 587, os.Getenv("SMTP_MAIL"), os.Getenv("SMTP_PASS"))
|
||||
if err := d.DialAndSend(m); err != nil {
|
||||
log.Fatalln(err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user