Fix: buildable

This commit is contained in:
Grail Finder
2025-07-02 15:51:14 +03:00
parent 9973546aad
commit 3e0d24f5f8
7 changed files with 110 additions and 105 deletions

View File

@ -2,14 +2,12 @@ package handlers
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"gralias/broker" "gralias/broker"
"gralias/llmapi" "gralias/llmapi"
"gralias/models" "gralias/models"
"gralias/wordloader" "gralias/wordloader"
"strings"
) )
func createRoom(ctx context.Context, req *models.RoomReq) (*models.Room, error) { func createRoom(ctx context.Context, req *models.RoomReq) (*models.Room, error) {
@ -20,7 +18,7 @@ func createRoom(ctx context.Context, req *models.RoomReq) (*models.Room, error)
} }
room := req.CreateRoom(creator) room := req.CreateRoom(creator)
room.RoomLink = cfg.BaseURL + "/room-join?id=" + room.ID room.RoomLink = cfg.BaseURL + "/room-join?id=" + room.ID
if err := repo.CreateRoom(ctx, room); err != nil { if err := repo.RoomCreate(ctx, room); err != nil {
return nil, err return nil, err
} }
return room, nil return room, nil
@ -109,25 +107,24 @@ func notifyBotIfNeeded(room *models.Room) {
// return nil // return nil
// } // }
func getAllNames() []string { // func getAllNames() []string {
names := []string{} // names := []string{}
// will not scale // // will not scale
wholeMemStore := memcache.GetAll() // session := &models.Session{}
session := &models.Session{} // // filter by key size only sessions
// filter by key size only sessions // for _, name := range wholeMemStore {
for k, v := range wholeMemStore { // // xid is 20 in len
// xid is 20 in len // if len(k) != 20 {
if len(k) != 20 { // continue
continue // }
} // if err := json.Unmarshal(v, &session); err != nil {
if err := json.Unmarshal(v, &session); err != nil { // log.Error("failed to unmarshal", "error", err)
log.Error("failed to unmarshal", "error", err) // continue
continue // }
} // names = append(names, session.Username)
names = append(names, session.Username) // }
} // return names
return names // }
}
// can room exists without state? I think no // can room exists without state? I think no
func getFullInfoByCtx(ctx context.Context) (*models.FullInfo, error) { func getFullInfoByCtx(ctx context.Context) (*models.FullInfo, error) {
@ -142,7 +139,7 @@ func getFullInfoByCtx(ctx context.Context) (*models.FullInfo, error) {
return resp, nil return resp, nil
} }
// room, err := getRoomByID(state.RoomID) // room, err := getRoomByID(state.RoomID)
room, err := repo.GetRoomByID(ctx, state.RoomID) room, err := repo.RoomGetByID(ctx, state.RoomID)
if err != nil { if err != nil {
log.Warn("failed to find room despite knowing room_id;", log.Warn("failed to find room despite knowing room_id;",
"room_id", state.RoomID) "room_id", state.RoomID)
@ -158,7 +155,7 @@ func getPlayerByCtx(ctx context.Context) (*models.Player, error) {
log.Debug("no username in ctx") log.Debug("no username in ctx")
return &models.Player{}, errors.New("no username in ctx") return &models.Player{}, errors.New("no username in ctx")
} }
return repo.GetPlayerByName(username) return repo.PlayerGetByName(username)
} }
// // DEPRECATED // // DEPRECATED
@ -249,41 +246,15 @@ func joinTeam(ctx context.Context, role, team string) (*models.FullInfo, error)
// } // }
// get bots // get bots
func listBots() map[string]map[string]string { func listBots() []models.Player {
cacheMap := memcache.GetAll() bots, err := repo.PlayerList(true)
resp := make(map[string]map[string]string) if err != nil {
// no way to know if room is public until unmarshal -_-; log.Error("failed to fetch bots from db", "error", err)
for key, value := range cacheMap {
if strings.HasPrefix(key, models.CacheBotPredix) {
botMap := make(map[string]string)
if err := json.Unmarshal(value, &botMap); err != nil {
log.Warn("failed to unmarshal bot", "error", err)
continue
}
resp[botMap["bot_name"]] = botMap
}
} }
return resp return bots
} }
// get players // get players
func listPlayers() map[string]map[string]string {
cacheMap := memcache.GetAll()
resp := make(map[string]map[string]string)
// no way to know if room is public until unmarshal -_-;
for key, value := range cacheMap {
if strings.HasPrefix(key, models.CacheStatePrefix) {
playerMap := make(map[string]string)
if err := json.Unmarshal(value, &playerMap); err != nil {
log.Warn("failed to unmarshal player", "error", err)
continue
}
resp[playerMap["Username"]] = playerMap
}
}
return resp
}
func notify(event, msg string) { func notify(event, msg string) {
Notifier.Notifier <- broker.NotificationEvent{ Notifier.Notifier <- broker.NotificationEvent{
EventName: event, EventName: event,
@ -312,54 +283,54 @@ func loadCards(room *models.Room) {
func recoverBots() { func recoverBots() {
bots := listBots() bots := listBots()
for botName, botMap := range bots { for _, bot := range bots {
if err := recoverBot(botMap); err != nil { if err := recoverBot(bot); err != nil {
log.Warn("failed to recover bot", "botName", botName, "error", err) log.Warn("failed to recover bot", "botName", bot.Username, "error", err)
} }
} }
} }
func recoverBot(bm map[string]string) error { func recoverBot(bm models.Player) error {
// check if room still exists // check if room still exists
if _, err := getRoomByID(bm["room_id"]); err != nil { if _, err := repo.RoomGetByID(context.Background(), bm.RoomID); err != nil {
return fmt.Errorf("no such room: %s; err: %w", bm["room_id"], err) return fmt.Errorf("no such room: %s; err: %w", bm.RoomID, err)
} }
log.Debug("recovering bot", "bot", bm) log.Debug("recovering bot", "bot", bm)
_, err := llmapi.NewBot(bm["role"], bm["team"], bm["bot_name"], bm["room_id"], cfg, true) _, err := llmapi.NewBot(string(bm.Role), string(bm.Team), bm.Username, bm.RoomID, cfg, true)
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
func recoverPlayers() { // func recoverPlayers() {
players := listPlayers() // players := listPlayers()
for playerName, playerMap := range players { // for playerName, playerMap := range players {
if err := recoverPlayer(playerMap); err != nil { // if err := recoverPlayer(playerMap); err != nil {
log.Warn("failed to recover player", "playerName", playerName, "error", err) // log.Warn("failed to recover player", "playerName", playerName, "error", err)
} // }
} // }
} // }
func recoverPlayer(pm map[string]string) error { // func recoverPlayer(pm map[string]string) error {
// check if room still exists // // check if room still exists
room, err := getRoomByID(pm["RoomID"]) // room, err := repo.RoomGetByID(context.Background(), pm["RoomID"])
if err != nil { // if err != nil {
return fmt.Errorf("no such room: %s; err: %w", pm["RoomID"], err) // return fmt.Errorf("no such room: %s; err: %w", pm["RoomID"], err)
} // }
log.Debug("recovering player", "player", pm) // log.Debug("recovering player", "player", pm)
role, team, ok := room.GetPlayerByName(pm["Username"]) // role, team, ok := room.GetPlayerByName(pm["Username"])
if !ok { // if !ok {
return fmt.Errorf("failed to find player %s in the room %v", pm["Username"], room) // return fmt.Errorf("failed to find player %s in the room %v", pm["Username"], room)
} // }
us := &models.UserState{ // us := &models.Player{
Username: pm["Username"], // Username: pm["Username"],
RoomID: pm["RoomID"], // RoomID: pm["RoomID"],
Team: team, // Team: team,
Role: role, // Role: role,
} // }
return saveState(pm["Username"], us) // return saveState(pm["Username"], us)
} // }
// validateMove checks if it is players turn // validateMove checks if it is players turn
func validateMove(fi *models.FullInfo, ur models.UserRole) error { func validateMove(fi *models.FullInfo, ur models.UserRole) error {

View File

@ -7,6 +7,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"gralias/models" "gralias/models"
"gralias/pkg/cache"
"gralias/utils" "gralias/utils"
"html/template" "html/template"
"net/http" "net/http"
@ -36,7 +37,12 @@ func HandleNameCheck(w http.ResponseWriter, r *http.Request) {
return return
} }
cleanName := utils.RemoveSpacesFromStr(username) cleanName := utils.RemoveSpacesFromStr(username)
allNames := getAllNames() // allNames := getAllNames()
allNames, err := repo.PlayerListNames()
if err != nil {
abortWithError(w, err.Error())
return
}
log.Info("names check", "taken_names", allNames, "trying_name", cleanName) log.Info("names check", "taken_names", allNames, "trying_name", cleanName)
tmpl, err := template.ParseGlob("components/*.html") tmpl, err := template.ParseGlob("components/*.html")
if err != nil { if err != nil {
@ -175,7 +181,7 @@ func makeCookie(username string, remote string) (*http.Cookie, error) {
} }
func cacheGetSession(key string) (*models.Session, error) { func cacheGetSession(key string) (*models.Session, error) {
userSessionB, err := memcache.Get(key) userSessionB, err := cache.MemCache.Get(key)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -191,7 +197,7 @@ func cacheSetSession(key string, session *models.Session) error {
if err != nil { if err != nil {
return err return err
} }
memcache.Set(key, sesb) cache.MemCache.Set(key, sesb)
memcache.Expire(key, cfg.SessionLifetime) cache.MemCache.Expire(key, cfg.SessionLifetime)
return nil return nil
} }

View File

@ -4,13 +4,11 @@ import (
"gralias/broker" "gralias/broker"
"gralias/config" "gralias/config"
"gralias/models" "gralias/models"
"gralias/pkg/cache"
"gralias/repos" "gralias/repos"
"html/template" "html/template"
"log/slog" "log/slog"
"net/http" "net/http"
"os" "os"
"time"
) )
var ( var (
@ -29,13 +27,13 @@ func init() {
// memcache = cache.MemCache // memcache = cache.MemCache
cfg = config.LoadConfigOrDefault("") cfg = config.LoadConfigOrDefault("")
Notifier = broker.Notifier Notifier = broker.Notifier
cache.MemCache.StartBackupRoutine(15 * time.Second) // Reduced backup interval // cache.MemCache.StartBackupRoutine(15 * time.Second) // Reduced backup interval
// bot loader // bot loader
// check the rooms if it has bot_{digits} in them, create bots if have // check the rooms if it has bot_{digits} in them, create bots if have
repo = repos.NewRepoProvider("sqlite3://../gralias.db") repo = repos.NewRepoProvider("sqlite3://../gralias.db")
recoverBots() recoverBots()
// if player has a roomID, but no team and role, try to recover // if player has a roomID, but no team and role, try to recover
recoverPlayers() // recoverPlayers()
} }
func HandlePing(w http.ResponseWriter, r *http.Request) { func HandlePing(w http.ResponseWriter, r *http.Request) {

View File

@ -6,6 +6,7 @@ import (
"crypto/sha256" "crypto/sha256"
"encoding/base64" "encoding/base64"
"gralias/models" "gralias/models"
"gralias/pkg/cache"
"net/http" "net/http"
) )
@ -70,7 +71,7 @@ func GetSession(next http.Handler) http.Handler {
return return
} }
if userSession.IsExpired() { if userSession.IsExpired() {
memcache.RemoveKey(sessionToken) cache.MemCache.RemoveKey(sessionToken)
msg := "session is expired" msg := "session is expired"
log.Debug(msg, "error", err, "token", sessionToken) log.Debug(msg, "error", err, "token", sessionToken)
next.ServeHTTP(w, r) next.ServeHTTP(w, r)

View File

@ -1,6 +1,7 @@
package handlers package handlers
import ( import (
"context"
"fmt" "fmt"
"gralias/models" "gralias/models"
"sync" "sync"
@ -35,7 +36,7 @@ func StartTurnTimer(roomID string, duration time.Duration) {
case <-done: case <-done:
return return
case <-ticker.C: case <-ticker.C:
room, err := getRoomByID(roomID) room, err := repo.RoomGetByID(context.Background(), roomID)
if err != nil { if err != nil {
log.Error("failed to get room by id", "error", err) log.Error("failed to get room by id", "error", err)
StopTurnTimer(roomID) StopTurnTimer(roomID)
@ -45,7 +46,7 @@ func StartTurnTimer(roomID string, duration time.Duration) {
log.Info("turn time is over", "room_id", roomID) log.Info("turn time is over", "room_id", roomID)
room.ChangeTurn() room.ChangeTurn()
room.MimeDone = false room.MimeDone = false
if err := saveRoom(room); err != nil { if err := repo.RoomUpdate(context.Background(), room); err != nil {
log.Error("failed to save room", "error", err) log.Error("failed to save room", "error", err)
} }
notify(models.NotifyTurnTimerPrefix+room.ID, fmt.Sprintf("%d", room.Settings.TurnSecondsLeft)) notify(models.NotifyTurnTimerPrefix+room.ID, fmt.Sprintf("%d", room.Settings.TurnSecondsLeft))
@ -72,4 +73,3 @@ func StopTurnTimer(roomID string) {
delete(timers, roomID) delete(timers, roomID)
} }
} }

View File

@ -378,7 +378,7 @@ type WordCard struct {
Word string `json:"word" db:"word"` Word string `json:"word" db:"word"`
Color WordColor `json:"color" db:"color"` Color WordColor `json:"color" db:"color"`
Revealed bool `json:"revealed" db:"revealed"` Revealed bool `json:"revealed" db:"revealed"`
MimeView bool `json:"mime_view" db:"mime_view"` // user who sees that card is mime Mime bool `json:"mime" db:"mime"` // user who sees that card is mime
Mark []CardMark `json:"marks" db:"-"` Mark []CardMark `json:"marks" db:"-"`
} }
@ -402,13 +402,16 @@ type RoomReq struct {
func (rr *RoomReq) CreateRoom(creator string) *Room { func (rr *RoomReq) CreateRoom(creator string) *Room {
roomID := xid.New().String() roomID := xid.New().String()
settings := GameSettings{
Language: rr.Language,
RoundTime: rr.RoundTime,
RoomPass: rr.RoomPass,
}
return &Room{ return &Room{
ID: roomID, ID: roomID,
CreatedAt: time.Now(), CreatedAt: time.Now(),
CreatorName: creator, CreatorName: creator,
Language: rr.Language, Settings: settings,
RoundTime: rr.RoundTime,
RoomPass: rr.RoomPass,
BotMap: make(map[string]BotPlayer), BotMap: make(map[string]BotPlayer),
} }
} }

View File

@ -12,6 +12,17 @@ type PlayersRepo interface {
PlayerDelete(roomID, username string) error PlayerDelete(roomID, username string) error
PlayerSetRoomID(username, roomID string) error PlayerSetRoomID(username, roomID string) error
PlayerExitRoom(username string) error PlayerExitRoom(username string) error
PlayerListNames() ([]string, error)
PlayerList(isBot bool) ([]models.Player, error)
}
func (p *RepoProvider) PlayerListNames() ([]string, error) {
var names []string
err := p.DB.SelectContext(context.Background(), &names, "SELECT username FROM players;")
if err != nil {
return nil, err
}
return names, nil
} }
func (p *RepoProvider) PlayerGetByName(username string) (*models.Player, error) { func (p *RepoProvider) PlayerGetByName(username string) (*models.Player, error) {
@ -49,3 +60,18 @@ func (p *RepoProvider) PlayerExitRoom(username string) error {
_, err := p.DB.ExecContext(context.Background(), "UPDATE players SET room_id = null WHERE username = ?", username) _, err := p.DB.ExecContext(context.Background(), "UPDATE players SET room_id = null WHERE username = ?", username)
return err return err
} }
func (p *RepoProvider) PlayerList(isBot bool) ([]models.Player, error) {
var players []models.Player
var query string
if isBot {
query = "SELECT id, room_id, username, team, role, is_bot FROM players WHERE is_bot = true;"
} else {
query = "SELECT id, room_id, username, team, role, is_bot FROM players WHERE is_bot = false;"
}
err := p.DB.SelectContext(context.Background(), &players, query)
if err != nil {
return nil, err
}
return players, nil
}