From 3d9de8ba114f3810401c16a79dc37d15e41dced2 Mon Sep 17 00:00:00 2001 From: Cipher Vance Date: Thu, 18 Sep 2025 20:06:36 -0500 Subject: [PATCH] feat: add user service layer with business logic - Create services/user_service.go with user creation and verification - Add input validation for username, email, and password - Implement duplicate user checking and password hashing - Add comprehensive error handling and logging --- services/UserService/user.py | 60 ----------------------------- services/email_service.go | 34 +++++++++++++++++ services/user_service.go | 73 ++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 60 deletions(-) delete mode 100644 services/UserService/user.py create mode 100644 services/email_service.go create mode 100644 services/user_service.go diff --git a/services/UserService/user.py b/services/UserService/user.py deleted file mode 100644 index 6f1c030..0000000 --- a/services/UserService/user.py +++ /dev/null @@ -1,60 +0,0 @@ -from models.User.user import User -from models.UserProfile.user_profile import UserProfile -from models import db -import re - -class UserService: - def create_user(self, username, password, email=None, first_name=None, last_name=None): - if not username or not password: - raise ValueError("Username and password are required") - - if email: - email_regex = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' - if not re.match(email_regex, email): - raise ValueError("Invalid email format") - - existing_user = User.query.filter( - (User.username == username) | (User.email == email) - ).first() - - if existing_user: - if existing_user.username == username: - raise ValueError("Username already exists") - else: - raise ValueError("Email already exists") - - if len(password) < 8: - raise ValueError("Password must be at least 8 characters long") - - try: - new_user = User( - username=username, - email=email or "", - password=password - ) - - db.session.add(new_user) - db.session.flush() - - user_profile = UserProfile( - user_id=new_user.id, - first_name=first_name or "", - last_name=last_name or "", - bio="", - profile_picture="" - ) - - db.session.add(user_profile) - db.session.commit() - - return new_user - - except Exception as e: - db.session.rollback() - raise Exception(f"Error creating user: {str(e)}") - - def verify_user(self, username, password): - user = User.query.filter_by(username=username).first() - if not user or not user.check_password(password): - raise ValueError("Invalid username or password") - return user \ No newline at end of file diff --git a/services/email_service.go b/services/email_service.go new file mode 100644 index 0000000..30a8c93 --- /dev/null +++ b/services/email_service.go @@ -0,0 +1,34 @@ +package services + +import ( + "fmt" + "net/smtp" + "os" +) + +type EmailService struct { + smtpHost string + smtpPort string + smtpUser string + smtpPassword string +} + +func NewEmailService() *EmailService { + return &EmailService{ + smtpHost: os.Getenv("SMTP_SERVER"), + smtpPort: os.Getenv("SMTP_PORT"), + smtpUser: os.Getenv("SMTP_USER"), + smtpPassword: os.Getenv("SMTP_PASSWORD"), + } +} + +func (e *EmailService) SendEmail(to, subject, body string) error { + from := e.smtpUser + + msg := fmt.Sprintf("From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s", from, to, subject, body) + + auth := smtp.PlainAuth("", e.smtpUser, e.smtpPassword, e.smtpHost) + addr := fmt.Sprintf("%s:%s", e.smtpHost, e.smtpPort) + + return smtp.SendMail(addr, auth, from, []string{to}, []byte(msg)) +} diff --git a/services/user_service.go b/services/user_service.go new file mode 100644 index 0000000..1640cb4 --- /dev/null +++ b/services/user_service.go @@ -0,0 +1,73 @@ +package services + +import ( + "errors" + "log" + "strings" + + "github.com/rideaware/rideaware-api/models" + "gorm.io/gorm" +) + +type UserService struct { + db *gorm.DB +} + +func NewUserService(db *gorm.DB) *UserService { + return &UserService{db: db} +} + +func (s *UserService) CreateUser(username, email, password string) (*models.User, error) { + if username == "" || email == "" || password == "" { + return nil, errors.New("username, email, and password are required") + } + + if len(username) < 3 || len(password) < 8 { + return nil, errors.New("username must be at least 3 characters and password must be at least 8 characters") + } + + // Basic email validation + if !strings.Contains(email, "@") { + return nil, errors.New("invalid email format") + } + + // Check if user exists (by username or email) + var existingUser models.User + if err := s.db.Where("username = ? OR email = ?", username, email).First(&existingUser).Error; err == nil { + return nil, errors.New("user with this username or email already exists") + } + + // Create new user + user := models.User{ + Username: username, + Email: email, + } + if err := user.SetPassword(password); err != nil { + log.Printf("Error hashing password: %v", err) + return nil, errors.New("could not create user") + } + + if err := s.db.Create(&user).Error; err != nil { + log.Printf("Error creating user: %v", err) + return nil, errors.New("could not create user") + } + + return &user, nil +} + +func (s *UserService) VerifyUser(username, password string) (*models.User, error) { + var user models.User + // Allow login with either username or email + if err := s.db.Where("username = ? OR email = ?", username, username).First(&user).Error; err != nil { + log.Printf("User not found: %s", username) + return nil, errors.New("invalid username or password") + } + + if !user.CheckPassword(password) { + log.Printf("Invalid password for user: %s", username) + return nil, errors.New("invalid username or password") + } + + log.Printf("User verified: %s", username) + return &user, nil +}