Full refactor of codebase

This commit is contained in:
Jonas Hahn
2025-09-18 16:51:57 +02:00
parent 0e6e48cd7b
commit 4168e92601
34 changed files with 1176 additions and 1062 deletions

76
src/repository/crud.go Normal file
View File

@@ -0,0 +1,76 @@
package repository
import (
"errors"
"log"
"strings"
"github.com/ascyii/qrank/src/utils"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func FindUser(ref any) *User {
// Session case
if c, ok := ref.(*gin.Context); ok {
session := sessions.Default(c)
if uid := session.Get("user_id"); uid != nil {
var u User
if err := db.First(&u, uid.(uint)).Error; err == nil {
return &u
}
return nil
}
}
// String handle case
if h, ok := ref.(string); ok {
h = strings.TrimSpace(h)
if h == "" {
return nil
}
var u User
var err error
if strings.Contains(h, "@") {
err = db.Where("LOWER(email) = ?", strings.ToLower(h)).First(&u).Error
} else {
err = db.Where("username = ?", h).First(&u).Error
}
if err == nil {
return &u
}
}
return nil
}
func FindOrCreateUserFromEmail(email string) *User {
var u User
// Try to find existing user
err := db.Where("email = ?", email).First(&u).Error // TODO: This makes bad logs
if err == nil {
return &u
}
if !errors.Is(err, gorm.ErrRecordNotFound) {
log.Println("Find user:", err)
return nil
}
// Create a new user
u = User{
Email: email,
Username: utils.DefaultUsername(),
}
if err := EnsureUniqueUsernameAndSlug(&u); err != nil {
log.Println("Ensure unique:", err)
}
if err := db.Create(&u).Error; err != nil {
log.Println("Create user:", err)
return nil
}
return &u
}

View File

@@ -0,0 +1,32 @@
package repository
import (
"log"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
var db *gorm.DB
func init() {
var err error
// Open connection to sqlite
db, err = gorm.Open(sqlite.Open("qrank.db"), &gorm.Config{})
if err != nil {
log.Fatal("Sqlite connect:", err)
}
// Migrate the database with gorm
if err := db.AutoMigrate(&User{}, &LoginToken{}, &Table{}, &Game{}, &GameUser{}); err != nil {
log.Fatal("Migrate:", err)
}
if err := db.SetupJoinTable(&User{}, "Games", &GameUser{}); err != nil {
log.Fatal("Setup jointable:", err)
}
}
func GetDB() *gorm.DB {
return db
}

32
src/repository/lookup.go Normal file
View File

@@ -0,0 +1,32 @@
package repository
import (
"errors"
"github.com/ascyii/qrank/src/utils"
)
func EnsureUniqueUsernameAndSlug(u *User) error {
baseU := u.Username
baseS := utils.Slugify(u.Username)
for i := range 5 {
candU := baseU
candS := baseS
if i > 0 {
suffix := "-" + utils.MustRandToken(1)
candU = baseU + suffix
candS = baseS + suffix
}
var cnt int64
db.Model(&User{}).Where("username = ?", candU).Count(&cnt)
if cnt == 0 {
db.Model(&User{}).Where("slug = ?", candS).Count(&cnt)
if cnt == 0 {
u.Username = candU
u.Slug = candS
return nil
}
}
}
return errors.New("cannot create unique username/slug")
}

67
src/repository/models.go Normal file
View File

@@ -0,0 +1,67 @@
package repository
import (
"time"
"gorm.io/gorm"
)
// One knows that the user is active when there exists a session for the user
type User struct {
gorm.Model
Email string
Username string
Slug string
Elo float64
GameCount int
WinCount int
LossCount int
Games []Game `gorm:"many2many:game_user"`
LastLogin time.Time
Active bool
}
type Table struct {
gorm.Model
Name string
Slug string
GameCount int
}
type Game struct {
gorm.Model
TableID *uint
Table *Table
Users []User `gorm:"many2many:game_user"`
ScoreA int
ScoreB int
}
// Join table between game and user with extra fields
type GameUser struct {
gorm.Model
GameID uint
Game Game
UserID uint
User User
Side string `gorm:"size:1"`
DeltaElo float64
}
type LoginToken struct {
gorm.Model
Token string
Email string
ExpiresAt time.Time
}

42
src/repository/session.go Normal file
View File

@@ -0,0 +1,42 @@
package repository
import (
"encoding/json"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
)
var store cookie.Store
func init() {
store = cookie.NewStore([]byte("secret"))
}
func SetupStore(r *gin.Engine) {
r.Use(sessions.Sessions("mysession", store))
}
// SaveForm stores submitted POST form data into the session
func SaveForm(c *gin.Context) {
session := sessions.Default(c)
form := map[string]string{}
for key, values := range c.Request.PostForm {
if len(values) > 0 {
form[key] = values[0]
}
}
if b, err := json.Marshal(form); err == nil {
session.Set("form_data", string(b))
session.Save()
}
}
// SaveForm stores submitted POST form data into the session
func SetMessage(c *gin.Context, message string) {
session := sessions.Default(c)
session.Set("message", message)
session.Save()
}