Files
a301_server/internal/bossraid/repository.go
tolelom d597ef2d46
All checks were successful
Server CI/CD / deploy (push) Successful in 1m31s
fix: 보안·안정성·동시성 개선 3차
- 입력 검증 강화 (로그인/체인 핸들러 전체)
- boss raid 비관적 잠금으로 동시성 문제 해결
- SSAFY 사용자명 sanitize + 트랜잭션 처리
- constant-time API 키 비교, 보안 헤더, graceful shutdown
- 안전하지 않은 기본값 경고 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:40:06 +09:00

64 lines
1.7 KiB
Go

package bossraid
import (
"strings"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type Repository struct {
db *gorm.DB
}
func NewRepository(db *gorm.DB) *Repository {
return &Repository{db: db}
}
func (r *Repository) Create(room *BossRoom) error {
return r.db.Create(room).Error
}
func (r *Repository) Update(room *BossRoom) error {
return r.db.Save(room).Error
}
func (r *Repository) FindBySessionName(sessionName string) (*BossRoom, error) {
var room BossRoom
if err := r.db.Where("session_name = ?", sessionName).First(&room).Error; err != nil {
return nil, err
}
return &room, nil
}
// FindBySessionNameForUpdate acquires a row-level lock (SELECT ... FOR UPDATE)
// to prevent concurrent state transitions.
func (r *Repository) FindBySessionNameForUpdate(sessionName string) (*BossRoom, error) {
var room BossRoom
if err := r.db.Clauses(clause.Locking{Strength: "UPDATE"}).Where("session_name = ?", sessionName).First(&room).Error; err != nil {
return nil, err
}
return &room, nil
}
// Transaction wraps a function in a database transaction.
func (r *Repository) Transaction(fn func(txRepo *Repository) error) error {
return r.db.Transaction(func(tx *gorm.DB) error {
return fn(&Repository{db: tx})
})
}
// CountActiveByUsername checks if a player is already in an active boss raid.
func (r *Repository) CountActiveByUsername(username string) (int64, error) {
var count int64
// LIKE 특수문자 이스케이프
escaped := strings.NewReplacer("%", "\\%", "_", "\\_").Replace(username)
search := `"` + escaped + `"`
err := r.db.Model(&BossRoom{}).
Where("status IN ? AND players LIKE ?",
[]RoomStatus{StatusWaiting, StatusInProgress},
"%"+search+"%",
).Count(&count).Error
return count, err
}