MIF_E31222379_BE/internal/authentication/authentication_service.go

815 lines
24 KiB
Go

package authentication
import (
"context"
"fmt"
"log"
"strings"
"time"
"rijig/internal/role"
"rijig/model"
"rijig/utils"
)
type AuthenticationService interface {
GetRegistrationStatus(ctx context.Context, userID, deviceID string) (*AuthResponse, error)
LoginAdmin(ctx context.Context, req *LoginAdminRequest) (*OTPAdminResponse, error)
RegisterAdmin(ctx context.Context, req *RegisterAdminRequest) (*EmailVerificationResponse, error)
VerifyAdminOTP(ctx context.Context, req *VerifyAdminOTPRequest) (*AuthResponse, error)
ResendAdminOTP(ctx context.Context, req *ResendAdminOTPRequest) (*OTPAdminResponse, error)
ForgotPassword(ctx context.Context, req *ForgotPasswordRequest) (*ResetPasswordResponse, error)
ResetPassword(ctx context.Context, req *ResetPasswordRequest) error
VerifyEmail(ctx context.Context, req *VerifyEmailRequest) error
ResendEmailVerification(ctx context.Context, req *ResendVerificationRequest) (*EmailVerificationResponse, error)
SendRegistrationOTP(ctx context.Context, req *LoginorRegistRequest) (*OTPResponse, error)
VerifyRegistrationOTP(ctx context.Context, req *VerifyOtpRequest) (*AuthResponse, error)
SendLoginOTP(ctx context.Context, req *LoginorRegistRequest) (*OTPResponse, error)
VerifyLoginOTP(ctx context.Context, req *VerifyOtpRequest) (*AuthResponse, error)
LogoutAuthentication(ctx context.Context, userID, deviceID string) error
}
type authenticationService struct {
authRepo AuthenticationRepository
roleRepo role.RoleRepository
emailService *utils.EmailService
}
func NewAuthenticationService(authRepo AuthenticationRepository, roleRepo role.RoleRepository) AuthenticationService {
return &authenticationService{
authRepo: authRepo,
roleRepo: roleRepo,
emailService: utils.NewEmailService(),
}
}
// func normalizeRoleName(roleName string) string {
// switch strings.ToLower(roleName) {
// case "administrator", "admin":
// return utils.RoleAdministrator
// case "pengelola":
// return utils.RolePengelola
// case "pengepul":
// return utils.RolePengepul
// case "masyarakat":
// return utils.RoleMasyarakat
// default:
// return strings.ToLower(roleName)
// }
// }
type GetRegistrationStatusResponse struct {
UserID string `json:"userId"`
RegistrationStatus string `json:"registrationStatus"`
RegistrationProgress int8 `json:"registrationProgress"`
Name string `json:"name"`
Phone string `json:"phone"`
Role string `json:"role"`
}
func (s *authenticationService) GetRegistrationStatus(ctx context.Context, userID, deviceID string) (*AuthResponse, error) {
user, err := s.authRepo.FindUserByID(ctx, userID)
if err != nil {
return nil, fmt.Errorf("user not found: %w", err)
}
if user.Role.RoleName == "" {
return nil, fmt.Errorf("user role not found")
}
if user.RegistrationStatus == utils.RegStatusPending {
log.Printf("⏳ User %s (%s) registration is still pending approval", user.Name, user.Phone)
return &AuthResponse{
Message: "Your registration is currently under review. Please wait for approval.",
RegistrationStatus: user.RegistrationStatus,
NextStep: "wait_for_approval",
}, nil
}
if user.RegistrationStatus == utils.RegStatusConfirmed || user.RegistrationStatus == utils.RegStatusRejected {
tokenResponse, err := utils.GenerateTokenPair(
user.ID,
user.Role.RoleName,
deviceID,
user.RegistrationStatus,
int(user.RegistrationProgress),
)
if err != nil {
log.Printf("GenerateTokenPair error: %v", err)
return nil, fmt.Errorf("failed to generate token: %v", err)
}
nextStep := utils.GetNextRegistrationStep(
user.Role.RoleName,
int(user.RegistrationProgress),
user.RegistrationStatus,
)
var message string
if user.RegistrationStatus == utils.RegStatusConfirmed {
message = "Registration approved successfully"
log.Printf("✅ User %s (%s) registration approved - generating tokens", user.Name, user.Phone)
} else if user.RegistrationStatus == utils.RegStatusRejected {
message = "Registration has been rejected"
log.Printf("❌ User %s (%s) registration rejected - generating tokens for rejection flow", user.Name, user.Phone)
}
return &AuthResponse{
Message: message,
AccessToken: tokenResponse.AccessToken,
RefreshToken: tokenResponse.RefreshToken,
TokenType: string(tokenResponse.TokenType),
ExpiresIn: tokenResponse.ExpiresIn,
RegistrationStatus: user.RegistrationStatus,
NextStep: nextStep,
SessionID: tokenResponse.SessionID,
}, nil
}
return nil, fmt.Errorf("unsupported registration status: %s", user.RegistrationStatus)
}
func (s *authenticationService) LoginAdmin(ctx context.Context, req *LoginAdminRequest) (*OTPAdminResponse, error) {
user, err := s.authRepo.FindUserByEmail(ctx, req.Email)
if err != nil {
return nil, fmt.Errorf("invalid credentials")
}
if user.Role == nil || user.Role.RoleName != "administrator" {
return nil, fmt.Errorf("invalid credentials")
}
if user.RegistrationStatus != "completed" {
return nil, fmt.Errorf("account not activated")
}
if !utils.CompareHashAndPlainText(user.Password, req.Password) {
return nil, fmt.Errorf("invalid credentials")
}
if utils.IsOTPValid(req.Email) {
remaining, _ := utils.GetOTPRemainingTime(req.Email)
return &OTPAdminResponse{
Message: "OTP sudah dikirim sebelumnya",
Email: req.Email,
ExpiresIn: remaining,
RemainingTime: formatDuration(remaining),
CanResend: false,
MaxAttempts: utils.MAX_OTP_ATTEMPTS,
}, nil
}
otp, err := utils.GenerateOTP()
if err != nil {
return nil, fmt.Errorf("failed to generate OTP")
}
if err := utils.StoreOTP(req.Email, otp); err != nil {
return nil, fmt.Errorf("failed to store OTP")
}
if err := s.emailService.SendOTPEmail(req.Email, user.Name, otp); err != nil {
log.Printf("Failed to send OTP email: %v", err)
return nil, fmt.Errorf("failed to send OTP email")
}
return &OTPAdminResponse{
Message: "Kode OTP berhasil dikirim ke email Anda",
Email: req.Email,
ExpiresIn: utils.OTP_EXPIRY,
RemainingTime: formatDuration(utils.OTP_EXPIRY),
CanResend: false,
MaxAttempts: utils.MAX_OTP_ATTEMPTS,
}, nil
}
func (s *authenticationService) VerifyAdminOTP(ctx context.Context, req *VerifyAdminOTPRequest) (*AuthResponse, error) {
if err := utils.ValidateOTP(req.Email, req.OTP); err != nil {
return nil, err
}
user, err := s.authRepo.FindUserByEmail(ctx, req.Email)
if err != nil {
return nil, fmt.Errorf("user not found")
}
if !user.EmailVerified {
user.EmailVerified = true
if err := s.authRepo.UpdateUser(ctx, user); err != nil {
log.Printf("Failed to update email verification status: %v", err)
}
}
token, err := utils.GenerateTokenPair(
user.ID,
user.Role.RoleName,
req.DeviceID,
user.RegistrationStatus,
int(user.RegistrationProgress),
)
if err != nil {
return nil, fmt.Errorf("failed to generate token: %w", err)
}
return &AuthResponse{
Message: "Login berhasil",
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
RegistrationStatus: user.RegistrationStatus,
SessionID: token.SessionID,
}, nil
}
func (s *authenticationService) ResendAdminOTP(ctx context.Context, req *ResendAdminOTPRequest) (*OTPAdminResponse, error) {
user, err := s.authRepo.FindUserByEmail(ctx, req.Email)
if err != nil {
return nil, fmt.Errorf("email not found")
}
if user.Role == nil || user.Role.RoleName != "administrator" {
return nil, fmt.Errorf("not authorized")
}
if utils.IsOTPValid(req.Email) {
remaining, _ := utils.GetOTPRemainingTime(req.Email)
return nil, fmt.Errorf("OTP masih berlaku. Tunggu %s untuk kirim ulang", formatDuration(remaining))
}
otp, err := utils.GenerateOTP()
if err != nil {
return nil, fmt.Errorf("failed to generate OTP")
}
if err := utils.StoreOTP(req.Email, otp); err != nil {
return nil, fmt.Errorf("failed to store OTP")
}
if err := s.emailService.SendOTPEmail(req.Email, user.Name, otp); err != nil {
log.Printf("Failed to send OTP email: %v", err)
return nil, fmt.Errorf("failed to send OTP email")
}
return &OTPAdminResponse{
Message: "Kode OTP baru berhasil dikirim",
Email: req.Email,
ExpiresIn: utils.OTP_EXPIRY,
RemainingTime: formatDuration(utils.OTP_EXPIRY),
CanResend: false,
MaxAttempts: utils.MAX_OTP_ATTEMPTS,
}, nil
}
func (s *authenticationService) VerifyEmail(ctx context.Context, req *VerifyEmailRequest) error {
verificationData, err := utils.ValidateEmailVerificationToken(req.Email, req.Token)
if err != nil {
return err
}
user, err := s.authRepo.FindUserByEmail(ctx, req.Email)
if err != nil {
return fmt.Errorf("user not found")
}
if user.ID != verificationData.UserID {
return fmt.Errorf("invalid verification token")
}
if user.EmailVerified {
return fmt.Errorf("email sudah terverifikasi sebelumnya")
}
user.EmailVerified = true
user.RegistrationStatus = utils.RegStatusComplete
// user.RegistrationProgress = 3
if err := s.authRepo.UpdateUser(ctx, user); err != nil {
return fmt.Errorf("failed to update user verification status: %w", err)
}
if err := utils.MarkEmailVerificationTokenAsUsed(req.Email); err != nil {
log.Printf("Failed to mark verification token as used: %v", err)
}
return nil
}
func (s *authenticationService) ResendEmailVerification(ctx context.Context, req *ResendVerificationRequest) (*EmailVerificationResponse, error) {
user, err := s.authRepo.FindUserByEmail(ctx, req.Email)
if err != nil {
return nil, fmt.Errorf("email not found")
}
if user.Role == nil || user.Role.RoleName != "administrator" {
return nil, fmt.Errorf("not authorized")
}
if user.EmailVerified {
return nil, fmt.Errorf("email sudah terverifikasi")
}
if utils.IsEmailVerificationTokenValid(req.Email) {
remaining, _ := utils.GetEmailVerificationTokenRemainingTime(req.Email)
return &EmailVerificationResponse{
Message: "Email verifikasi sudah dikirim sebelumnya",
Email: req.Email,
ExpiresIn: remaining,
RemainingTime: formatDuration(remaining),
}, nil
}
token, err := utils.GenerateEmailVerificationToken()
if err != nil {
return nil, fmt.Errorf("failed to generate verification token")
}
if err := utils.StoreEmailVerificationToken(req.Email, user.ID, token); err != nil {
return nil, fmt.Errorf("failed to store verification token")
}
if err := s.emailService.SendEmailVerificationEmail(req.Email, user.Name, token); err != nil {
log.Printf("Failed to send verification email: %v", err)
return nil, fmt.Errorf("failed to send verification email")
}
return &EmailVerificationResponse{
Message: "Email verifikasi berhasil dikirim ulang",
Email: req.Email,
ExpiresIn: utils.EMAIL_VERIFICATION_TOKEN_EXPIRY,
RemainingTime: formatDuration(utils.EMAIL_VERIFICATION_TOKEN_EXPIRY),
}, nil
}
func (s *authenticationService) ForgotPassword(ctx context.Context, req *ForgotPasswordRequest) (*ResetPasswordResponse, error) {
user, err := s.authRepo.FindUserByEmail(ctx, req.Email)
if err != nil {
return &ResetPasswordResponse{
Message: "Jika email terdaftar, link reset password akan dikirim",
Email: req.Email,
ExpiresIn: utils.RESET_TOKEN_EXPIRY,
RemainingTime: formatDuration(utils.RESET_TOKEN_EXPIRY),
}, nil
}
if user.Role == nil || user.Role.RoleName != "administrator" {
return &ResetPasswordResponse{
Message: "Jika email terdaftar, link reset password akan dikirim",
Email: req.Email,
ExpiresIn: utils.RESET_TOKEN_EXPIRY,
RemainingTime: formatDuration(utils.RESET_TOKEN_EXPIRY),
}, nil
}
if utils.IsResetTokenValid(req.Email) {
remaining, _ := utils.GetResetTokenRemainingTime(req.Email)
return &ResetPasswordResponse{
Message: "Link reset password sudah dikirim sebelumnya",
Email: req.Email,
ExpiresIn: remaining,
RemainingTime: formatDuration(remaining),
}, nil
}
token, err := utils.GenerateResetToken()
if err != nil {
return nil, fmt.Errorf("failed to generate reset token")
}
if err := utils.StoreResetToken(req.Email, user.ID, token); err != nil {
return nil, fmt.Errorf("failed to store reset token")
}
if err := s.emailService.SendResetPasswordEmail(req.Email, user.Name, token); err != nil {
log.Printf("Failed to send reset password email: %v", err)
return nil, fmt.Errorf("failed to send reset password email")
}
return &ResetPasswordResponse{
Message: "Link reset password berhasil dikirim ke email Anda",
Email: req.Email,
ExpiresIn: utils.RESET_TOKEN_EXPIRY,
RemainingTime: formatDuration(utils.RESET_TOKEN_EXPIRY),
}, nil
}
func (s *authenticationService) ResetPassword(ctx context.Context, req *ResetPasswordRequest) error {
resetData, err := utils.ValidateResetToken(req.Email, req.Token)
if err != nil {
return err
}
user, err := s.authRepo.FindUserByEmail(ctx, req.Email)
if err != nil {
return fmt.Errorf("user not found")
}
if user.ID != resetData.UserID {
return fmt.Errorf("invalid reset token")
}
hashedPassword, err := utils.HashingPlainText(req.NewPassword)
if err != nil {
return fmt.Errorf("failed to hash password: %w", err)
}
user.Password = hashedPassword
if err := s.authRepo.UpdateUser(ctx, user); err != nil {
return fmt.Errorf("failed to update password: %w", err)
}
if err := utils.MarkResetTokenAsUsed(req.Email); err != nil {
log.Printf("Failed to mark reset token as used: %v", err)
}
if err := utils.RevokeAllRefreshTokens(user.ID); err != nil {
log.Printf("Failed to revoke refresh tokens: %v", err)
}
return nil
}
func (s *authenticationService) RegisterAdmin(ctx context.Context, req *RegisterAdminRequest) (*EmailVerificationResponse, error) {
existingUser, _ := s.authRepo.FindUserByEmail(ctx, req.Email)
if existingUser != nil {
return nil, fmt.Errorf("email already in use")
}
hashedPassword, err := utils.HashingPlainText(req.Password)
if err != nil {
return nil, fmt.Errorf("failed to hash password: %w", err)
}
role, err := s.roleRepo.FindRoleByName(ctx, "administrator")
if err != nil {
return nil, fmt.Errorf("role name not found: %w", err)
}
user := &model.User{
Name: req.Name,
Phone: req.Phone,
Email: req.Email,
Gender: req.Gender,
Dateofbirth: req.DateOfBirth,
Placeofbirth: req.PlaceOfBirth,
Password: hashedPassword,
RoleID: role.ID,
RegistrationStatus: "pending_email_verification",
RegistrationProgress: 1,
EmailVerified: false,
}
if err := s.authRepo.CreateUser(ctx, user); err != nil {
return nil, fmt.Errorf("failed to create user: %w", err)
}
token, err := utils.GenerateEmailVerificationToken()
if err != nil {
return nil, fmt.Errorf("failed to generate verification token")
}
if err := utils.StoreEmailVerificationToken(req.Email, user.ID, token); err != nil {
return nil, fmt.Errorf("failed to store verification token")
}
if err := s.emailService.SendEmailVerificationEmail(req.Email, user.Name, token); err != nil {
log.Printf("Failed to send verification email: %v", err)
return nil, fmt.Errorf("failed to send verification email")
}
return &EmailVerificationResponse{
Message: "Admin berhasil didaftarkan. Silakan cek email untuk verifikasi",
Email: req.Email,
ExpiresIn: utils.EMAIL_VERIFICATION_TOKEN_EXPIRY,
RemainingTime: formatDuration(utils.EMAIL_VERIFICATION_TOKEN_EXPIRY),
}, nil
}
func formatDuration(d time.Duration) string {
minutes := int(d.Minutes())
seconds := int(d.Seconds()) % 60
return fmt.Sprintf("%d:%02d", minutes, seconds)
}
func (s *authenticationService) SendRegistrationOTP(ctx context.Context, req *LoginorRegistRequest) (*OTPResponse, error) {
normalizedRole := strings.ToLower(req.RoleName)
existingUser, err := s.authRepo.FindUserByPhoneAndRole(ctx, req.Phone, normalizedRole)
if err == nil && existingUser != nil {
return nil, fmt.Errorf("nomor telepon dengan role %s sudah terdaftar", req.RoleName)
}
roleData, err := s.roleRepo.FindRoleByName(ctx, normalizedRole)
if err != nil {
return nil, fmt.Errorf("role tidak valid: %v", err)
}
rateLimitKey := fmt.Sprintf("otp_limit:%s", req.Phone)
if isRateLimited(rateLimitKey, 3, 5*time.Minute) {
return nil, fmt.Errorf("terlalu banyak permintaan OTP, coba lagi dalam 5 menit")
}
otp, err := utils.GenerateOTP()
if err != nil {
return nil, fmt.Errorf("gagal generate OTP: %v", err)
}
otpKey := fmt.Sprintf("otp:%s:register", req.Phone)
otpData := OTPData{
Phone: req.Phone,
OTP: otp,
Role: normalizedRole,
RoleID: roleData.ID,
Type: "register",
Attempts: 0,
ExpiresAt: time.Now().Add(90 * time.Second),
}
err = utils.SetCacheWithTTL(otpKey, otpData, 90*time.Second)
if err != nil {
return nil, fmt.Errorf("gagal menyimpan OTP: %v", err)
}
err = sendOTP(req.Phone, otp)
if err != nil {
return nil, fmt.Errorf("gagal mengirim OTP: %v", err)
}
return &OTPResponse{
Message: "OTP berhasil dikirim",
ExpiresIn: 90,
Phone: maskPhoneNumber(req.Phone),
}, nil
}
func (s *authenticationService) VerifyRegistrationOTP(ctx context.Context, req *VerifyOtpRequest) (*AuthResponse, error) {
otpKey := fmt.Sprintf("otp:%s:register", req.Phone)
var otpData OTPData
err := utils.GetCache(otpKey, &otpData)
if err != nil {
return nil, fmt.Errorf("OTP tidak ditemukan atau sudah kadaluarsa")
}
if otpData.Attempts >= 3 {
utils.DeleteCache(otpKey)
return nil, fmt.Errorf("terlalu banyak percobaan, silakan minta OTP baru")
}
if otpData.OTP != req.Otp {
otpData.Attempts++
utils.SetCacheWithTTL(otpKey, otpData, time.Until(otpData.ExpiresAt))
return nil, fmt.Errorf("kode OTP salah")
}
if otpData.Role != req.RoleName {
return nil, fmt.Errorf("role tidak sesuai")
}
normalizedRole := strings.ToLower(req.RoleName)
user := &model.User{
Phone: req.Phone,
PhoneVerified: true,
RoleID: otpData.RoleID,
RegistrationStatus: utils.RegStatusIncomplete,
RegistrationProgress: utils.ProgressOTPVerified,
Name: "",
Gender: "",
Dateofbirth: "",
Placeofbirth: "",
}
err = s.authRepo.CreateUser(ctx, user)
if err != nil {
return nil, fmt.Errorf("gagal membuat user: %v", err)
}
if user.ID == "" {
return nil, fmt.Errorf("gagal mendapatkan user ID setelah registrasi")
}
utils.DeleteCache(otpKey)
tokenResponse, err := utils.GenerateTokenPair(
user.ID,
normalizedRole,
req.DeviceID,
user.RegistrationStatus,
int(user.RegistrationProgress),
)
if err != nil {
return nil, fmt.Errorf("gagal generate token: %v", err)
}
nextStep := utils.GetNextRegistrationStep(
normalizedRole,
int(user.RegistrationProgress),
user.RegistrationStatus,
)
return &AuthResponse{
Message: "Registrasi berhasil",
AccessToken: tokenResponse.AccessToken,
RefreshToken: tokenResponse.RefreshToken,
TokenType: string(tokenResponse.TokenType),
ExpiresIn: tokenResponse.ExpiresIn,
RegistrationStatus: user.RegistrationStatus,
NextStep: nextStep,
SessionID: tokenResponse.SessionID,
}, nil
}
func (s *authenticationService) SendLoginOTP(ctx context.Context, req *LoginorRegistRequest) (*OTPResponse, error) {
user, err := s.authRepo.FindUserByPhone(ctx, req.Phone)
if err != nil {
return nil, fmt.Errorf("nomor telepon tidak terdaftar")
}
if !user.PhoneVerified {
return nil, fmt.Errorf("nomor telepon belum diverifikasi")
}
rateLimitKey := fmt.Sprintf("otp_limit:%s", req.Phone)
if isRateLimited(rateLimitKey, 3, 5*time.Minute) {
return nil, fmt.Errorf("terlalu banyak permintaan OTP, coba lagi dalam 5 menit")
}
otp, err := utils.GenerateOTP()
if err != nil {
return nil, fmt.Errorf("gagal generate OTP: %v", err)
}
otpKey := fmt.Sprintf("otp:%s:login", req.Phone)
otpData := OTPData{
Phone: req.Phone,
OTP: otp,
UserID: user.ID,
Role: user.Role.RoleName,
Type: "login",
Attempts: 0,
}
err = utils.SetCacheWithTTL(otpKey, otpData, 1*time.Minute)
if err != nil {
return nil, fmt.Errorf("gagal menyimpan OTP: %v", err)
}
err = sendOTP(req.Phone, otp)
if err != nil {
return nil, fmt.Errorf("gagal mengirim OTP: %v", err)
}
return &OTPResponse{
Message: "OTP berhasil dikirim",
ExpiresIn: 300,
Phone: maskPhoneNumber(req.Phone),
}, nil
}
func (s *authenticationService) VerifyLoginOTP(ctx context.Context, req *VerifyOtpRequest) (*AuthResponse, error) {
otpKey := fmt.Sprintf("otp:%s:login", req.Phone)
var otpData OTPData
err := utils.GetCache(otpKey, &otpData)
if err != nil {
return nil, fmt.Errorf("OTP tidak ditemukan atau sudah kadaluarsa")
}
if otpData.Attempts >= 3 {
utils.DeleteCache(otpKey)
return nil, fmt.Errorf("terlalu banyak percobaan, silakan minta OTP baru")
}
if otpData.OTP != req.Otp {
otpData.Attempts++
utils.SetCache(otpKey, otpData, time.Until(otpData.ExpiresAt))
return nil, fmt.Errorf("kode OTP salah")
}
normalizedRole := strings.ToLower(req.RoleName)
user, err := s.authRepo.FindUserByPhoneAndRole(ctx, req.Phone, normalizedRole)
if err != nil {
return nil, fmt.Errorf("user tidak ditemukan")
}
utils.DeleteCache(otpKey)
tokenResponse, err := utils.GenerateTokenPair(
user.ID,
normalizedRole,
req.DeviceID,
user.RegistrationStatus,
int(user.RegistrationProgress),
)
if err != nil {
return nil, fmt.Errorf("gagal generate token: %v", err)
}
nextStep := utils.GetNextRegistrationStep(
normalizedRole,
int(user.RegistrationProgress),
user.RegistrationStatus,
)
var message string
if user.RegistrationStatus == utils.RegStatusComplete {
message = "verif pin"
nextStep = "verif_pin"
} else {
message = "otp berhasil diverifikasi"
}
return &AuthResponse{
Message: message,
AccessToken: tokenResponse.AccessToken,
RefreshToken: tokenResponse.RefreshToken,
TokenType: string(tokenResponse.TokenType),
ExpiresIn: tokenResponse.ExpiresIn,
RegistrationStatus: user.RegistrationStatus,
NextStep: nextStep,
SessionID: tokenResponse.SessionID,
}, nil
}
func (s *authenticationService) LogoutAuthentication(ctx context.Context, userID, deviceID string) error {
if err := utils.RevokeRefreshToken(userID, deviceID); err != nil {
return fmt.Errorf("failed to revoke token: %w", err)
}
return nil
}
func maskPhoneNumber(phone string) string {
if len(phone) < 4 {
return phone
}
return phone[:4] + strings.Repeat("*", len(phone)-8) + phone[len(phone)-4:]
}
func isRateLimited(key string, maxAttempts int, duration time.Duration) bool {
var count int
err := utils.GetCache(key, &count)
if err != nil {
count = 0
}
if count >= maxAttempts {
return true
}
count++
utils.SetCache(key, count, duration)
return false
}
func sendOTP(phone, otp string) error {
fmt.Printf("Sending OTP %s to %s\n", otp, phone)
return nil
}
// func convertUserToResponse(user *model.User) *UserResponse {
// return &UserResponse{
// ID: user.ID,
// Name: user.Name,
// Phone: user.Phone,
// Email: user.Email,
// Role: user.Role.RoleName,
// RegistrationStatus: user.RegistrationStatus,
// RegistrationProgress: user.RegistrationProgress,
// PhoneVerified: user.PhoneVerified,
// Avatar: user.Avatar,
// }
// }
func IsRegistrationComplete(role string, progress int) bool {
switch role {
case "masyarakat":
return progress >= 1
case "pengepul":
return progress >= 2
case "pengelola":
return progress >= 3
}
return false
}