// internal/services/auth_service.go package services import ( "errors" "log" // ← TAMBAHKAN INI "lost-and-found/internal/config" "lost-and-found/internal/models" "lost-and-found/internal/repositories" "lost-and-found/internal/utils" "gorm.io/gorm" ) type AuthService struct { userRepo *repositories.UserRepository roleRepo *repositories.RoleRepository auditLogRepo *repositories.AuditLogRepository } func NewAuthService(db *gorm.DB) *AuthService { return &AuthService{ userRepo: repositories.NewUserRepository(db), roleRepo: repositories.NewRoleRepository(db), auditLogRepo: repositories.NewAuditLogRepository(db), } } // RegisterRequest represents registration data type RegisterRequest struct { Name string `json:"name" binding:"required"` Email string `json:"email" binding:"required,email"` Password string `json:"password" binding:"required,min=6"` NRP string `json:"nrp"` Phone string `json:"phone"` } // LoginRequest represents login data type LoginRequest struct { Email string `json:"email" binding:"required,email"` Password string `json:"password" binding:"required"` } // AuthResponse represents authentication response type AuthResponse struct { Token string `json:"token"` User models.UserResponse `json:"user"` } // Register registers a new user func (s *AuthService) Register(req RegisterRequest, ipAddress, userAgent string) (*AuthResponse, error) { // Check if email already exists existingUser, _ := s.userRepo.FindByEmail(req.Email) if existingUser != nil { return nil, errors.New("email already registered") } // Check if NRP already exists if req.NRP != "" { existingNRP, _ := s.userRepo.FindByNRP(req.NRP) if existingNRP != nil { return nil, errors.New("NRP already registered") } } // Hash password hashedPassword, err := utils.HashPassword(req.Password) if err != nil { return nil, errors.New("failed to hash password") } // Get user role ID userRole, err := s.roleRepo.FindByName(models.RoleUser) if err != nil { return nil, errors.New("failed to get user role") } // Create user user := &models.User{ Name: req.Name, Email: req.Email, Password: hashedPassword, NRP: req.NRP, Phone: req.Phone, RoleID: userRole.ID, Status: "active", } if err := s.userRepo.Create(user); err != nil { return nil, errors.New("failed to create user") } // Load user with role user, err = s.userRepo.FindByID(user.ID) if err != nil { return nil, err } // Generate JWT token token, err := config.GenerateToken(user.ID, user.Email, user.Role.Name) if err != nil { return nil, errors.New("failed to generate token") } // Log audit s.auditLogRepo.Log(&user.ID, models.ActionCreate, models.EntityUser, &user.ID, "User registered", ipAddress, userAgent) return &AuthResponse{ Token: token, User: user.ToResponse(), }, nil } // Login authenticates a user func (s *AuthService) Login(req LoginRequest, ipAddress, userAgent string) (*AuthResponse, error) { // DEBUG: Print request info log.Printf("🔍 [LOGIN] Attempting login for email: %s", req.Email) log.Printf("🔍 [LOGIN] Password length: %d characters", len(req.Password)) // Find user by email user, err := s.userRepo.FindByEmail(req.Email) if err != nil { log.Printf("❌ [LOGIN] User not found in database: %s - Error: %v", req.Email, err) return nil, errors.New("invalid email or password") } log.Printf("✅ [LOGIN] User found in DB - ID: %d, Name: %s, Email: %s", user.ID, user.Name, user.Email) log.Printf("🔍 [LOGIN] User role ID: %d", user.RoleID) log.Printf("🔍 [LOGIN] User status: %s", user.Status) log.Printf("🔍 [LOGIN] Password hash from DB: %s", user.Password) // Check if user is blocked if user.IsBlocked() { log.Printf("❌ [LOGIN] User is blocked: %s", user.Email) return nil, errors.New("account is blocked") } log.Printf("✅ [LOGIN] User is active, checking password...") // Verify password log.Printf("🔍 [LOGIN] Checking password: '%s' against hash", req.Password) passwordMatch := utils.CheckPasswordHash(req.Password, user.Password) log.Printf("🔍 [LOGIN] Password match result: %v", passwordMatch) if !passwordMatch { log.Printf("❌ [LOGIN] Password mismatch for user: %s", user.Email) return nil, errors.New("invalid email or password") } log.Printf("✅ [LOGIN] Password correct! Generating token...") // Check if Role is loaded if user.Role.ID == 0 { log.Printf("⚠️ [LOGIN] Role not loaded! RoleID: %d, Role.Name: %s", user.RoleID, user.Role.Name) log.Printf("🔧 [LOGIN] Attempting to load role manually...") // Reload user with role user, err = s.userRepo.FindByID(user.ID) if err != nil { log.Printf("❌ [LOGIN] Failed to reload user with role: %v", err) return nil, errors.New("failed to load user data") } log.Printf("✅ [LOGIN] Role reloaded - Role.ID: %d, Role.Name: %s", user.Role.ID, user.Role.Name) } else { log.Printf("✅ [LOGIN] Role loaded - ID: %d, Name: %s", user.Role.ID, user.Role.Name) } // Generate JWT token token, err := config.GenerateToken(user.ID, user.Email, user.Role.Name) if err != nil { log.Printf("❌ [LOGIN] Failed to generate token: %v", err) return nil, errors.New("failed to generate token") } log.Printf("✅ [LOGIN] Token generated successfully for %s (Role: %s)", user.Email, user.Role.Name) // Log audit s.auditLogRepo.Log(&user.ID, models.ActionLogin, models.EntityUser, &user.ID, "User logged in", ipAddress, userAgent) log.Printf("✅ [LOGIN] Audit log created") log.Printf("🎉 [LOGIN] Login successful for %s - Returning response", user.Email) return &AuthResponse{ Token: token, User: user.ToResponse(), }, nil } // ValidateToken validates JWT token and returns user func (s *AuthService) ValidateToken(tokenString string) (*models.User, error) { // Validate token claims, err := config.ValidateToken(tokenString) if err != nil { return nil, errors.New("invalid token") } // Get user user, err := s.userRepo.FindByID(claims.UserID) if err != nil { return nil, errors.New("user not found") } // Check if user is blocked if user.IsBlocked() { return nil, errors.New("account is blocked") } return user, nil } // RefreshToken refreshes JWT token func (s *AuthService) RefreshToken(oldToken string) (string, error) { return config.RefreshToken(oldToken) }