diff --git a/crons/main.go b/crons/main.go index fb788f8..89c4aaf 100644 --- a/crons/main.go +++ b/crons/main.go @@ -2,6 +2,8 @@ package crons import ( "context" + "database/sql" + "errors" "gralias/repos" "log/slog" "time" @@ -25,6 +27,7 @@ func (cm *CronManager) Start() { for range ticker.C { cm.CleanupRooms() cm.CleanupActions() + cm.CleanupInactiveRooms() } }() } @@ -133,3 +136,51 @@ func (cm *CronManager) CleanupActions() { cm.log.Error("failed to commit transaction for actions cleanup", "err", err) } } + +func (cm *CronManager) CleanupInactiveRooms() { + ctx, tx, err := cm.repo.InitTx(context.Background()) + if err != nil { + cm.log.Error("failed to init transaction for inactive rooms cleanup", "err", err) + return + } + defer func() { + if r := recover(); r != nil { + if err := tx.Rollback(); err != nil { + cm.log.Error("failed to rollback transaction for inactive rooms cleanup", "err", err) + } + panic(r) + } + }() + rooms, err := cm.repo.RoomList(ctx) + if err != nil { + cm.log.Error("failed to get rooms list for inactive rooms cleanup", "err", err) + if err := tx.Rollback(); err != nil { + cm.log.Error("failed to rollback transaction for inactive rooms cleanup", "err", err) + } + return + } + for _, room := range rooms { + lastActionTime, err := cm.repo.ActionGetLastTimeByRoomID(ctx, room.ID) + if err != nil && !errors.Is(err, sql.ErrNoRows) { + cm.log.Error("failed to get last action time for room", "room_id", room.ID, "err", err) + continue + } + if lastActionTime.IsZero() && time.Since(room.CreatedAt) > time.Hour { + cm.log.Info("deleting inactive room (no actions)", "room_id", room.ID, "created_at", room.CreatedAt) + if err := cm.repo.RoomDeleteByID(ctx, room.ID); err != nil { + cm.log.Error("failed to delete inactive room (no actions)", "room_id", room.ID, "err", err) + } + continue + } + if !lastActionTime.IsZero() && time.Since(lastActionTime) > time.Hour && time.Since(room.CreatedAt) > time.Hour { + cm.log.Info("deleting inactive room (last action older than 1 hour)", "room_id", room.ID, "last_action_time", lastActionTime) + if err := cm.repo.RoomDeleteByID(ctx, room.ID); err != nil { + cm.log.Error("failed to delete inactive room (last action older than 1 hour)", "room_id", room.ID, "err", err) + } + } + } + if err := tx.Commit(); err != nil { + cm.log.Error("failed to commit transaction for inactive rooms cleanup", "err", err) + } +} + diff --git a/repos/actions.go b/repos/actions.go index facbff6..320248b 100644 --- a/repos/actions.go +++ b/repos/actions.go @@ -3,6 +3,7 @@ package repos import ( "context" "gralias/models" + "time" "github.com/jmoiron/sqlx" ) @@ -13,6 +14,7 @@ type ActionsRepo interface { ActionGetLastClue(ctx context.Context, roomID string) (*models.Action, error) ActionDeleteByRoomID(ctx context.Context, roomID string) error ActionDeleteOrphaned(ctx context.Context) error + ActionGetLastTimeByRoomID(ctx context.Context, roomID string) (time.Time, error) } func (p *RepoProvider) ActionList(ctx context.Context, roomID string) ([]models.Action, error) { @@ -26,10 +28,20 @@ func (p *RepoProvider) ActionList(ctx context.Context, roomID string) ([]models. func (p *RepoProvider) ActionCreate(ctx context.Context, a *models.Action) error { db := getDB(ctx, p.DB) - _, err := db.ExecContext(ctx, `INSERT INTO actions (room_id, actor, actor_color, action_type, word, word_color, number_associated, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, a.RoomID, a.Actor, a.ActorColor, a.Action, a.Word, a.WordColor, a.Number, a.CreatedAt.UnixNano()) + _, err := db.ExecContext(ctx, `INSERT INTO actions (room_id, actor, actor_color, action_type, word, word_color, number_associated, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, a.RoomID, a.Actor, a.ActorColor, a.Action, a.Word, a.WordColor, a.Number, time.Now()) return err } +func (p *RepoProvider) ActionGetLastTimeByRoomID(ctx context.Context, roomID string) (time.Time, error) { + lastTime := time.Time{} + err := sqlx.GetContext(ctx, p.DB, &lastTime, + `SELECT created_at FROM actions WHERE room_id = ? ORDER BY created_at DESC LIMIT 1`, roomID) + if err != nil { + return lastTime, err + } + return lastTime, nil +} + func (p *RepoProvider) ActionGetLastClue(ctx context.Context, roomID string) (*models.Action, error) { action := &models.Action{} err := sqlx.GetContext(ctx, p.DB, action, `SELECT actor, actor_color, action_type, word, word_color, number_associated, created_at FROM actions WHERE room_id = ? AND action_type = 'gave_clue' ORDER BY created_at DESC LIMIT 1`, roomID) diff --git a/repos/card_marks.go b/repos/card_marks.go index 2fc3608..255d0ca 100644 --- a/repos/card_marks.go +++ b/repos/card_marks.go @@ -14,33 +14,25 @@ type CardMarksRepo interface { CardMarksByRoomID(ctx context.Context, roomID string) ([]models.CardMark, error) } -type cardMarksRepo struct { - db *sqlx.DB -} - -func NewCardMarksRepo(db *sqlx.DB) CardMarksRepo { - return &cardMarksRepo{db: db} -} - -func (r *cardMarksRepo) CardMarksByCardID(ctx context.Context, cardID uint32) ([]models.CardMark, error) { +func (r *RepoProvider) CardMarksByCardID(ctx context.Context, cardID uint32) ([]models.CardMark, error) { var cardMarks []models.CardMark - err := sqlx.SelectContext(ctx, getDB(ctx, r.db), &cardMarks, "SELECT * FROM card_marks WHERE card_id = ?", cardID) + err := sqlx.SelectContext(ctx, getDB(ctx, r.DB), &cardMarks, "SELECT * FROM card_marks WHERE card_id = ?", cardID) return cardMarks, err } -func (r *cardMarksRepo) CardMarksAdd(ctx context.Context, cm *models.CardMark) error { - _, err := getDB(ctx, r.db).ExecContext(ctx, "INSERT INTO card_marks (card_id, username) VALUES (?, ?)", cm.CardID, cm.Username) +func (r *RepoProvider) CardMarksAdd(ctx context.Context, cm *models.CardMark) error { + _, err := getDB(ctx, r.DB).ExecContext(ctx, "INSERT INTO card_marks (card_id, username) VALUES (?, ?)", cm.CardID, cm.Username) return err } -func (r *cardMarksRepo) CardMarksRemove(ctx context.Context, cardID uint32, username string) error { - db := getDB(ctx, r.db) +func (r *RepoProvider) CardMarksRemove(ctx context.Context, cardID uint32, username string) error { + db := getDB(ctx, r.DB) _, err := db.ExecContext(ctx, "DELETE FROM card_marks WHERE card_id = ? AND username = ?", cardID, username) return err } -func (r *cardMarksRepo) CardMarksByRoomID(ctx context.Context, roomID string) ([]models.CardMark, error) { +func (r *RepoProvider) CardMarksByRoomID(ctx context.Context, roomID string) ([]models.CardMark, error) { var cardMarks []models.CardMark - err := sqlx.SelectContext(ctx, getDB(ctx, r.db), &cardMarks, "SELECT * FROM card_marks WHERE card_id IN (select id from word_cards where room_id = ?)", roomID) + err := sqlx.SelectContext(ctx, getDB(ctx, r.DB), &cardMarks, "SELECT * FROM card_marks WHERE card_id IN (select id from word_cards where room_id = ?)", roomID) return cardMarks, err } diff --git a/repos/main.go b/repos/main.go index c639c54..20ef961 100644 --- a/repos/main.go +++ b/repos/main.go @@ -26,7 +26,6 @@ type RepoProvider struct { DB *sqlx.DB mu sync.RWMutex pathToDB string - CardMarksRepo } func NewRepoProvider(pathToDB string) *RepoProvider { @@ -44,7 +43,6 @@ func NewRepoProvider(pathToDB string) *RepoProvider { rp := &RepoProvider{ DB: db, pathToDB: pathToDB, - CardMarksRepo: NewCardMarksRepo(db), } go rp.pingLoop()