package handler import ( "accounting-app/pkg/api" "accounting-app/internal/service" "net/http" "github.com/gin-gonic/gin" ) // AppLockHandler handles HTTP requests for app lock type AppLockHandler struct { service *service.AppLockService } // NewAppLockHandler creates a new app lock handler func NewAppLockHandler(service *service.AppLockService) *AppLockHandler { return &AppLockHandler{service: service} } // SetPasswordRequest represents the request to set app lock password type SetPasswordRequest struct { Password string `json:"password" binding:"required,min=4"` } // VerifyPasswordRequest represents the request to verify app lock password type VerifyPasswordRequest struct { Password string `json:"password" binding:"required"` } // ChangePasswordRequest represents the request to change app lock password type ChangePasswordRequest struct { OldPassword string `json:"old_password" binding:"required"` NewPassword string `json:"new_password" binding:"required,min=4"` } // AppLockStatusResponse represents the app lock status response type AppLockStatusResponse struct { IsEnabled bool `json:"is_enabled"` IsLocked bool `json:"is_locked"` FailedAttempts int `json:"failed_attempts"` RemainingLockTime int `json:"remaining_lock_time"` // in seconds MaxAttempts int `json:"max_attempts"` } // GetStatus returns the current app lock status // @Summary Get app lock status // @Tags AppLock // @Produce json // @Success 200 {object} AppLockStatusResponse // @Router /api/v1/app-lock/status [get] func (h *AppLockHandler) GetStatus(c *gin.Context) { userId, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } status, err := h.service.GetStatus(userId.(uint)) if err != nil { api.Error(c, http.StatusInternalServerError, "GET_STATUS_FAILED", "Failed to get app lock status") return } remainingTime, _ := h.service.GetRemainingLockTime(userId.(uint)) response := AppLockStatusResponse{ IsEnabled: status.IsEnabled, IsLocked: status.IsLocked(), FailedAttempts: status.FailedAttempts, RemainingLockTime: remainingTime, MaxAttempts: service.MaxFailedAttempts, } api.Success(c, response) } // SetPassword sets or updates the app lock password // @Summary Set app lock password // @Tags AppLock // @Accept json // @Produce json // @Param request body SetPasswordRequest true "Password" // @Success 200 {object} api.Response // @Router /api/v1/app-lock/password [post] func (h *AppLockHandler) SetPassword(c *gin.Context) { var req SetPasswordRequest if err := c.ShouldBindJSON(&req); err != nil { api.Error(c, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request") return } userId, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } if err := h.service.SetPassword(userId.(uint), req.Password); err != nil { if err == service.ErrPasswordRequired { api.Error(c, http.StatusBadRequest, "PASSWORD_REQUIRED", "Password is required") return } api.Error(c, http.StatusInternalServerError, "SET_PASSWORD_FAILED", "Failed to set password") return } api.Success(c, gin.H{"message": "Password set successfully"}) } // VerifyPassword verifies the app lock password // @Summary Verify app lock password // @Tags AppLock // @Accept json // @Produce json // @Param request body VerifyPasswordRequest true "Password" // @Success 200 {object} api.Response // @Router /api/v1/app-lock/verify [post] func (h *AppLockHandler) VerifyPassword(c *gin.Context) { var req VerifyPasswordRequest if err := c.ShouldBindJSON(&req); err != nil { api.Error(c, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request") return } userId, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } err := h.service.VerifyPassword(userId.(uint), req.Password) if err != nil { if err == service.ErrAppLocked { remainingTime, _ := h.service.GetRemainingLockTime(userId.(uint)) c.JSON(http.StatusLocked, gin.H{ "success": false, "error": "App is locked", "remaining_lock_time": remainingTime, }) return } if err == service.ErrAppLockInvalidPassword { status, _ := h.service.GetStatus(userId.(uint)) c.JSON(http.StatusUnauthorized, gin.H{ "success": false, "error": "Invalid password", "failed_attempts": status.FailedAttempts, "max_attempts": service.MaxFailedAttempts, "remaining_attempts": service.MaxFailedAttempts - status.FailedAttempts, }) return } if err == service.ErrAppLockNotEnabled { api.Error(c, http.StatusBadRequest, "APP_LOCK_NOT_ENABLED", "App lock is not enabled") return } api.Error(c, http.StatusInternalServerError, "VERIFY_FAILED", "Failed to verify password") return } api.Success(c, gin.H{"message": "Password verified successfully"}) } // DisableLock disables the app lock // @Summary Disable app lock // @Tags AppLock // @Produce json // @Success 200 {object} api.Response // @Router /api/v1/app-lock/disable [post] func (h *AppLockHandler) DisableLock(c *gin.Context) { userId, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } if err := h.service.DisableLock(userId.(uint)); err != nil { api.Error(c, http.StatusInternalServerError, "DISABLE_FAILED", "Failed to disable app lock") return } api.Success(c, gin.H{"message": "App lock disabled successfully"}) } // ChangePassword changes the app lock password // @Summary Change app lock password // @Tags AppLock // @Accept json // @Produce json // @Param request body ChangePasswordRequest true "Old and new passwords" // @Success 200 {object} api.Response // @Router /api/v1/app-lock/password/change [post] func (h *AppLockHandler) ChangePassword(c *gin.Context) { var req ChangePasswordRequest if err := c.ShouldBindJSON(&req); err != nil { api.Error(c, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request") return } userId, exists := c.Get("user_id") if !exists { api.Unauthorized(c, "User not authenticated") return } err := h.service.ChangePassword(userId.(uint), req.OldPassword, req.NewPassword) if err != nil { if err == service.ErrAppLockInvalidPassword || err == service.ErrAppLocked { api.Error(c, http.StatusUnauthorized, "INVALID_OLD_PASSWORD", "Invalid old password") return } api.Error(c, http.StatusInternalServerError, "CHANGE_PASSWORD_FAILED", "Failed to change password") return } api.Success(c, gin.H{"message": "Password changed successfully"}) }