Files
Novault-backend/internal/handler/app_lock_handler.go
2026-01-25 21:59:00 +08:00

219 lines
6.5 KiB
Go

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"})
}