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
This commit is contained in:
		
							parent
							
								
									3ed698918b
								
							
						
					
					
						commit
						3d9de8ba11
					
				
					 3 changed files with 107 additions and 60 deletions
				
			
		|  | @ -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 |  | ||||||
							
								
								
									
										34
									
								
								services/email_service.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								services/email_service.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -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)) | ||||||
|  | } | ||||||
							
								
								
									
										73
									
								
								services/user_service.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								services/user_service.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Cipher Vance
						Cipher Vance