Feat: add cleaner cron
This commit is contained in:
@ -1,6 +1,7 @@
|
|||||||
BASE_URL = "https://localhost:3000"
|
BASE_URL = "https://localhost:3000"
|
||||||
SESSION_LIFETIME_SECONDS = 30000
|
SESSION_LIFETIME_SECONDS = 30000
|
||||||
COOKIE_SECRET = "test"
|
COOKIE_SECRET = "test"
|
||||||
|
DB_PATH = "sqlite3://gralias.db"
|
||||||
|
|
||||||
[SERVICE]
|
[SERVICE]
|
||||||
HOST = "localhost"
|
HOST = "localhost"
|
||||||
|
@ -13,6 +13,7 @@ type Config struct {
|
|||||||
SessionLifetime int64 `toml:"SESSION_LIFETIME_SECONDS"`
|
SessionLifetime int64 `toml:"SESSION_LIFETIME_SECONDS"`
|
||||||
CookieSecret string `toml:"COOKIE_SECRET"`
|
CookieSecret string `toml:"COOKIE_SECRET"`
|
||||||
LLMConfig LLMConfig `toml:"LLM"`
|
LLMConfig LLMConfig `toml:"LLM"`
|
||||||
|
DBPath string `toml:"DB_PATH"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
@ -38,6 +39,7 @@ func LoadConfigOrDefault(fn string) *Config {
|
|||||||
config.CookieSecret = "test"
|
config.CookieSecret = "test"
|
||||||
config.ServerConfig.Host = "localhost"
|
config.ServerConfig.Host = "localhost"
|
||||||
config.ServerConfig.Port = "3000"
|
config.ServerConfig.Port = "3000"
|
||||||
|
config.DBPath = "sqlite3://gralias.db"
|
||||||
}
|
}
|
||||||
fmt.Printf("config debug; config.LLMConfig.URL: %s\n", config.LLMConfig.URL)
|
fmt.Printf("config debug; config.LLMConfig.URL: %s\n", config.LLMConfig.URL)
|
||||||
return config
|
return config
|
||||||
|
76
crons/main.go
Normal file
76
crons/main.go
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package crons
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"gralias/repos"
|
||||||
|
"log/slog"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CronManager struct {
|
||||||
|
repo repos.AllRepos
|
||||||
|
log *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCronManager(repo repos.AllRepos, log *slog.Logger) *CronManager {
|
||||||
|
return &CronManager{
|
||||||
|
repo: repo,
|
||||||
|
log: log,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *CronManager) Start() {
|
||||||
|
ticker := time.NewTicker(30 * time.Second)
|
||||||
|
go func() {
|
||||||
|
for range ticker.C {
|
||||||
|
cm.CleanupRooms()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *CronManager) CleanupRooms() {
|
||||||
|
ctx := context.Background()
|
||||||
|
rooms, err := cm.repo.RoomList(ctx)
|
||||||
|
if err != nil {
|
||||||
|
cm.log.Error("failed to get rooms list", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, room := range rooms {
|
||||||
|
players, err := cm.repo.PlayerListByRoom(ctx, room.ID)
|
||||||
|
if err != nil {
|
||||||
|
cm.log.Error("failed to get players for room", "room_id", room.ID, "err", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(players) == 0 {
|
||||||
|
cm.log.Info("deleting empty room", "room_id", room.ID)
|
||||||
|
if err := cm.repo.RoomDeleteByID(ctx, room.ID); err != nil {
|
||||||
|
cm.log.Error("failed to delete empty room", "room_id", room.ID, "err", err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
creatorInRoom := false
|
||||||
|
for _, player := range players {
|
||||||
|
if player.Username == room.CreatorName {
|
||||||
|
creatorInRoom = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !creatorInRoom {
|
||||||
|
cm.log.Info("deleting room because creator left", "room_id", room.ID)
|
||||||
|
for _, player := range players {
|
||||||
|
if player.IsBot {
|
||||||
|
if err := cm.repo.PlayerDelete(ctx, room.ID, player.Username); err != nil {
|
||||||
|
cm.log.Error("failed to delete bot player", "room_id", room.ID, "username", player.Username, "err", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := cm.repo.PlayerExitRoom(ctx, player.Username); err != nil {
|
||||||
|
cm.log.Error("failed to update player room", "room_id", room.ID, "username", player.Username, "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := cm.repo.RoomDeleteByID(ctx, room.ID); err != nil {
|
||||||
|
cm.log.Error("failed to delete room after creator left", "room_id", room.ID, "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -168,7 +168,9 @@ func HandleStartGame(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
tx.Rollback()
|
if err := tx.Rollback(); err != nil {
|
||||||
|
log.Error("failed to rollback transaction", "error", err)
|
||||||
|
}
|
||||||
panic(r)
|
panic(r)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -189,7 +191,9 @@ func HandleStartGame(w http.ResponseWriter, r *http.Request) {
|
|||||||
fi.Room.ActionHistory = append(fi.Room.ActionHistory, action)
|
fi.Room.ActionHistory = append(fi.Room.ActionHistory, action)
|
||||||
// Use the new context with transaction
|
// Use the new context with transaction
|
||||||
if err := saveFullInfo(ctx, fi); err != nil {
|
if err := saveFullInfo(ctx, fi); err != nil {
|
||||||
tx.Rollback()
|
if err := tx.Rollback(); err != nil {
|
||||||
|
log.Error("failed to rollback transaction", "error", err)
|
||||||
|
}
|
||||||
abortWithError(w, err.Error())
|
abortWithError(w, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -197,7 +201,9 @@ func HandleStartGame(w http.ResponseWriter, r *http.Request) {
|
|||||||
action.RoomID = fi.Room.ID
|
action.RoomID = fi.Room.ID
|
||||||
action.CreatedAt = time.Now()
|
action.CreatedAt = time.Now()
|
||||||
if err := repo.CreateAction(ctx, fi.Room.ID, &action); err != nil {
|
if err := repo.CreateAction(ctx, fi.Room.ID, &action); err != nil {
|
||||||
tx.Rollback()
|
if err := tx.Rollback(); err != nil {
|
||||||
|
log.Error("failed to rollback transaction", "error", err)
|
||||||
|
}
|
||||||
log.Error("failed to save action", "error", err)
|
log.Error("failed to save action", "error", err)
|
||||||
abortWithError(w, err.Error())
|
abortWithError(w, err.Error())
|
||||||
return
|
return
|
||||||
@ -206,7 +212,9 @@ func HandleStartGame(w http.ResponseWriter, r *http.Request) {
|
|||||||
for _, card := range fi.Room.Cards {
|
for _, card := range fi.Room.Cards {
|
||||||
card.RoomID = fi.Room.ID // Ensure RoomID is set for each card
|
card.RoomID = fi.Room.ID // Ensure RoomID is set for each card
|
||||||
if err := repo.WordCardsCreate(ctx, &card); err != nil {
|
if err := repo.WordCardsCreate(ctx, &card); err != nil {
|
||||||
tx.Rollback()
|
if err := tx.Rollback(); err != nil {
|
||||||
|
log.Error("failed to rollback transaction", "error", err)
|
||||||
|
}
|
||||||
log.Error("failed to save word card", "error", err)
|
log.Error("failed to save word card", "error", err)
|
||||||
abortWithError(w, err.Error())
|
abortWithError(w, err.Error())
|
||||||
return
|
return
|
||||||
|
@ -70,7 +70,9 @@ func GetSession(next http.Handler) http.Handler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if userSession.IsExpired() {
|
if userSession.IsExpired() {
|
||||||
repo.SessionDelete(r.Context(), sessionToken)
|
if err := repo.SessionDelete(r.Context(), sessionToken); err != nil {
|
||||||
|
log.Error("failed to delete session", "error", err)
|
||||||
|
}
|
||||||
// cache.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)
|
||||||
|
8
main.go
8
main.go
@ -3,7 +3,9 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"gralias/config"
|
"gralias/config"
|
||||||
|
"gralias/crons"
|
||||||
"gralias/handlers"
|
"gralias/handlers"
|
||||||
|
"gralias/repos"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -61,6 +63,12 @@ func main() {
|
|||||||
stop := make(chan os.Signal, 1)
|
stop := make(chan os.Signal, 1)
|
||||||
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
|
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
||||||
|
repo := repos.NewRepoProvider(cfg.DBPath)
|
||||||
|
defer repo.Close()
|
||||||
|
|
||||||
|
cm := crons.NewCronManager(repo, slog.Default())
|
||||||
|
cm.Start()
|
||||||
|
|
||||||
server := ListenToRequests(cfg.ServerConfig.Port)
|
server := ListenToRequests(cfg.ServerConfig.Port)
|
||||||
go func() {
|
go func() {
|
||||||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||||
|
@ -104,3 +104,7 @@ func (p *RepoProvider) InitTx(ctx context.Context) (context.Context, *sqlx.Tx, e
|
|||||||
}
|
}
|
||||||
return context.WithValue(ctx, "tx", tx), tx, nil
|
return context.WithValue(ctx, "tx", tx), tx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *RepoProvider) Close() {
|
||||||
|
p.DB.Close()
|
||||||
|
}
|
||||||
|
@ -16,6 +16,7 @@ type PlayersRepo interface {
|
|||||||
PlayerExitRoom(ctx context.Context, username string) error
|
PlayerExitRoom(ctx context.Context, username string) error
|
||||||
PlayerListNames(ctx context.Context) ([]string, error)
|
PlayerListNames(ctx context.Context) ([]string, error)
|
||||||
PlayerList(ctx context.Context, isBot bool) ([]models.Player, error)
|
PlayerList(ctx context.Context, isBot bool) ([]models.Player, error)
|
||||||
|
PlayerListByRoom(ctx context.Context, roomID string) ([]models.Player, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *RepoProvider) PlayerListNames(ctx context.Context) ([]string, error) {
|
func (p *RepoProvider) PlayerListNames(ctx context.Context) ([]string, error) {
|
||||||
@ -85,3 +86,12 @@ func (p *RepoProvider) PlayerList(ctx context.Context, isBot bool) ([]models.Pla
|
|||||||
}
|
}
|
||||||
return players, nil
|
return players, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *RepoProvider) PlayerListByRoom(ctx context.Context, roomID string) ([]models.Player, error) {
|
||||||
|
var players []models.Player
|
||||||
|
err := sqlx.SelectContext(ctx, p.DB, &players, "SELECT id, room_id, username, team, role, is_bot FROM players WHERE room_id = ?", roomID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return players, nil
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user