package auth import ( "context" "fmt" "net/http" "github.com/google/uuid" "github.com/mattnite/forgejo-tickets/internal/email" "github.com/mattnite/forgejo-tickets/internal/models" "golang.org/x/crypto/bcrypt" "gorm.io/gorm" ) type Service struct { db *gorm.DB store *PGStore email *email.Client } func NewService(db *gorm.DB, store *PGStore, emailClient *email.Client) *Service { return &Service{ db: db, store: store, email: emailClient, } } func (s *Service) Register(ctx context.Context, emailAddr, password, name string) (*models.User, error) { hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return nil, fmt.Errorf("hash password: %w", err) } hashStr := string(hash) user := models.User{ Email: emailAddr, PasswordHash: &hashStr, Name: name, EmailVerified: false, } if err := s.db.WithContext(ctx).Create(&user).Error; err != nil { return nil, fmt.Errorf("create user: %w", err) } return &user, nil } func (s *Service) Login(ctx context.Context, emailAddr, password string) (*models.User, error) { var user models.User if err := s.db.WithContext(ctx).Where("email = ?", emailAddr).First(&user).Error; err != nil { return nil, fmt.Errorf("invalid email or password") } if user.PasswordHash == nil { return nil, fmt.Errorf("this account uses social login") } if err := bcrypt.CompareHashAndPassword([]byte(*user.PasswordHash), []byte(password)); err != nil { return nil, fmt.Errorf("invalid email or password") } if !user.EmailVerified { return nil, fmt.Errorf("please verify your email before logging in") } return &user, nil } func (s *Service) CreateSession(r *http.Request, w http.ResponseWriter, userID uuid.UUID) error { session, err := s.store.Get(r, sessionCookieName) if err != nil { session, err = s.store.New(r, sessionCookieName) if err != nil { return err } } session.Values["user_id"] = userID.String() return s.store.Save(r, w, session) } func (s *Service) DestroySession(r *http.Request, w http.ResponseWriter) error { session, err := s.store.Get(r, sessionCookieName) if err != nil { return nil } session.Options.MaxAge = -1 return s.store.Save(r, w, session) } func (s *Service) CreateUserWithPassword(ctx context.Context, emailAddr, password, name string, verified bool) (*models.User, error) { hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return nil, fmt.Errorf("hash password: %w", err) } hashStr := string(hash) user := models.User{ Email: emailAddr, PasswordHash: &hashStr, Name: name, EmailVerified: verified, } if err := s.db.WithContext(ctx).Create(&user).Error; err != nil { return nil, fmt.Errorf("create user: %w", err) } return &user, nil } func (s *Service) DB() *gorm.DB { return s.db }