215 lines
6.5 KiB
Go
215 lines
6.5 KiB
Go
// 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)
|
|
} |