package auth import ( "context" "errors" "fmt" "golang.org/x/crypto/bcrypt" "a301_game_server/internal/db" ) var ( ErrInvalidCredentials = errors.New("invalid credentials") ErrUsernameTaken = errors.New("username already taken") ) // Service handles account registration and authentication. type Service struct { pool *db.Pool } // NewService creates a new auth service. func NewService(pool *db.Pool) *Service { return &Service{pool: pool} } // Register creates a new account. Returns the account ID. func (s *Service) Register(ctx context.Context, username, password string) (int64, error) { hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return 0, fmt.Errorf("hash password: %w", err) } var id int64 err = s.pool.QueryRow(ctx, `INSERT INTO accounts (username, password) VALUES ($1, $2) ON CONFLICT (username) DO NOTHING RETURNING id`, username, string(hash), ).Scan(&id) if err != nil { return 0, ErrUsernameTaken } return id, nil } // Login validates credentials and returns the account ID. func (s *Service) Login(ctx context.Context, username, password string) (int64, error) { var id int64 var hash string err := s.pool.QueryRow(ctx, `SELECT id, password FROM accounts WHERE username = $1`, username, ).Scan(&id, &hash) if err != nil { return 0, ErrInvalidCredentials } if err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)); err != nil { return 0, ErrInvalidCredentials } return id, nil }