package handler import ( "github.com/go-playground/validator/v10" "github.com/google/uuid" "github.com/labstack/echo/v4" "tm/internal/domain" "tm/pkg/logger" "tm/pkg/response" ) // UserAuthHandler handles authentication related HTTP requests for panel users type UserAuthHandler struct { userAuthService domain.UserAuthService validator *validator.Validate logger logger.Logger } // NewUserAuthHandler creates a new panel user authentication handler func NewUserAuthHandler( userAuthService domain.UserAuthService, validator *validator.Validate, logger logger.Logger, ) *UserAuthHandler { return &UserAuthHandler{ userAuthService: userAuthService, validator: validator, logger: logger, } } // Login handles panel user authentication // @Summary Panel user login // @Description Authenticate panel user and return tokens // @Tags user-auth // @Accept json // @Produce json // @Param request body domain.LoginRequest true "Login request" // @Success 200 {object} response.APIResponse{data=domain.UserAuthResponse} // @Failure 400 {object} response.APIResponse // @Failure 401 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/admin/auth/login [post] func (h *UserAuthHandler) Login(c echo.Context) error { var req domain.LoginRequest // Bind request body if err := c.Bind(&req); err != nil { h.logger.Warn("Failed to bind panel user login request", map[string]interface{}{ "error": err.Error(), }) return response.BadRequest(c, "Invalid request format", err.Error()) } // Validate request if err := h.validator.Struct(&req); err != nil { h.logger.Warn("Panel user login request validation failed", map[string]interface{}{ "error": err.Error(), "email": req.Email, }) return response.ValidationError(c, "Validation failed", err.Error()) } // Call service authResponse, err := h.userAuthService.Login(c.Request().Context(), req) if err != nil { h.logger.Warn("Panel user login failed", map[string]interface{}{ "error": err.Error(), "email": req.Email, }) if err.Error() == "invalid credentials" { return response.Unauthorized(c, "Invalid email or password") } return response.InternalServerError(c, "Login failed") } h.logger.Info("Panel user logged in successfully", map[string]interface{}{ "user_id": authResponse.User.ID.String(), "email": authResponse.User.Email, "role": authResponse.User.Role, }) return response.Success(c, authResponse, "Login successful") } // RefreshToken handles token refresh for panel users // @Summary Refresh panel user access token // @Description Generate new access token using refresh token // @Tags user-auth // @Accept json // @Produce json // @Param request body RefreshTokenRequest true "Refresh token request" // @Success 200 {object} response.APIResponse{data=domain.UserAuthResponse} // @Failure 400 {object} response.APIResponse // @Failure 401 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/admin/auth/refresh [post] func (h *UserAuthHandler) RefreshToken(c echo.Context) error { var req struct { RefreshToken string `json:"refresh_token" validate:"required"` } // Bind request body if err := c.Bind(&req); err != nil { return response.BadRequest(c, "Invalid request format", err.Error()) } // Validate request if err := h.validator.Struct(&req); err != nil { return response.ValidationError(c, "Validation failed", err.Error()) } // Call service authResponse, err := h.userAuthService.RefreshToken(c.Request().Context(), req.RefreshToken) if err != nil { h.logger.Warn("Panel user token refresh failed", map[string]interface{}{ "error": err.Error(), }) if err.Error() == "invalid refresh token" { return response.Unauthorized(c, "Invalid refresh token") } return response.InternalServerError(c, "Token refresh failed") } return response.Success(c, authResponse, "Token refreshed successfully") } // ChangePassword handles password change for panel users // @Summary Change panel user password // @Description Change authenticated panel user's password // @Tags user-auth // @Accept json // @Produce json // @Security ApiKeyAuth // @Param request body ChangePasswordRequest true "Change password request" // @Success 200 {object} response.APIResponse // @Failure 400 {object} response.APIResponse // @Failure 401 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/admin/auth/change-password [post] func (h *UserAuthHandler) ChangePassword(c echo.Context) error { var req struct { OldPassword string `json:"old_password" validate:"required"` NewPassword string `json:"new_password" validate:"required,min=8"` } // Bind request body if err := c.Bind(&req); err != nil { return response.BadRequest(c, "Invalid request format", err.Error()) } // Validate request if err := h.validator.Struct(&req); err != nil { return response.ValidationError(c, "Validation failed", err.Error()) } // Get user from context (set by auth middleware) user, ok := c.Get("user").(*domain.User) if !ok { return response.Unauthorized(c, "Authentication required") } // Call service err := h.userAuthService.ChangePassword(c.Request().Context(), user.ID, req.OldPassword, req.NewPassword) if err != nil { h.logger.Error("Panel user password change failed", map[string]interface{}{ "error": err.Error(), "user_id": user.ID.String(), }) if err.Error() == "invalid old password" { return response.BadRequest(c, "Invalid old password", "") } return response.InternalServerError(c, "Password change failed") } h.logger.Info("Panel user password changed successfully", map[string]interface{}{ "user_id": user.ID.String(), }) return response.Success(c, nil, "Password changed successfully") } // GetProfile returns current panel user profile // @Summary Get panel user profile // @Description Get authenticated panel user's profile information // @Tags user-auth // @Produce json // @Security ApiKeyAuth // @Success 200 {object} response.APIResponse{data=domain.User} // @Failure 401 {object} response.APIResponse // @Router /api/admin/auth/profile [get] func (h *UserAuthHandler) GetProfile(c echo.Context) error { // Get user from context (set by auth middleware) user, ok := c.Get("user").(*domain.User) if !ok { return response.Unauthorized(c, "Authentication required") } return response.Success(c, user, "Profile retrieved successfully") } // CreatePanelUser handles creating new panel users (admin only) // @Summary Create new panel user // @Description Create a new panel user (admin only) // @Tags user-auth // @Accept json // @Produce json // @Security ApiKeyAuth // @Param request body domain.CreateUserRequest true "Create user request" // @Success 201 {object} response.APIResponse{data=domain.User} // @Failure 400 {object} response.APIResponse // @Failure 401 {object} response.APIResponse // @Failure 403 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/admin/users [post] func (h *UserAuthHandler) CreatePanelUser(c echo.Context) error { var req domain.CreateUserRequest // Bind request body if err := c.Bind(&req); err != nil { h.logger.Warn("Failed to bind create user request", map[string]interface{}{ "error": err.Error(), }) return response.BadRequest(c, "Invalid request format", err.Error()) } // Validate request if err := h.validator.Struct(&req); err != nil { h.logger.Warn("Create user request validation failed", map[string]interface{}{ "error": err.Error(), "email": req.Email, }) return response.ValidationError(c, "Validation failed", err.Error()) } // Get current user from context (set by auth middleware) currentUser, ok := c.Get("user").(*domain.User) if !ok { return response.Unauthorized(c, "Authentication required") } // Check if current user can create users if !currentUser.CanManageUsers() { return response.Forbidden(c, "Insufficient permissions to create users") } // Call service newUser, err := h.userAuthService.CreatePanelUser(c.Request().Context(), req, currentUser.ID) if err != nil { h.logger.Error("Panel user creation failed", map[string]interface{}{ "error": err.Error(), "email": req.Email, "created_by": currentUser.ID.String(), }) if err.Error() == "user already exists" { return response.Conflict(c, "User already exists") } return response.InternalServerError(c, "User creation failed") } h.logger.Info("Panel user created successfully", map[string]interface{}{ "user_id": newUser.ID.String(), "email": newUser.Email, "role": newUser.Role, "created_by": currentUser.ID.String(), }) return response.Created(c, newUser, "User created successfully") } // UpdateUserPermissions handles updating user permissions (admin only) // @Summary Update user permissions // @Description Update panel user permissions (admin only) // @Tags user-auth // @Accept json // @Produce json // @Security ApiKeyAuth // @Param user_id path string true "User ID" // @Param request body UpdatePermissionsRequest true "Update permissions request" // @Success 200 {object} response.APIResponse // @Failure 400 {object} response.APIResponse // @Failure 401 {object} response.APIResponse // @Failure 403 {object} response.APIResponse // @Failure 404 {object} response.APIResponse // @Failure 500 {object} response.APIResponse // @Router /api/admin/users/{user_id}/permissions [put] func (h *UserAuthHandler) UpdateUserPermissions(c echo.Context) error { var req struct { Permissions []string `json:"permissions" validate:"required"` } // Bind request body if err := c.Bind(&req); err != nil { return response.BadRequest(c, "Invalid request format", err.Error()) } // Validate request if err := h.validator.Struct(&req); err != nil { return response.ValidationError(c, "Validation failed", err.Error()) } // Get user ID from path userIDStr := c.Param("user_id") if userIDStr == "" { return response.BadRequest(c, "User ID is required", "") } userID, err := uuid.Parse(userIDStr) if err != nil { return response.BadRequest(c, "Invalid user ID format", "") } // Get current user from context (set by auth middleware) currentUser, ok := c.Get("user").(*domain.User) if !ok { return response.Unauthorized(c, "Authentication required") } // Check if current user can manage users if !currentUser.CanManageUsers() { return response.Forbidden(c, "Insufficient permissions to update user permissions") } // Call service err = h.userAuthService.UpdateUserPermissions(c.Request().Context(), userID, req.Permissions, currentUser.ID) if err != nil { h.logger.Error("User permissions update failed", map[string]interface{}{ "error": err.Error(), "user_id": userID.String(), "updated_by": currentUser.ID.String(), }) if err.Error() == "user not found" { return response.NotFound(c, "User not found") } return response.InternalServerError(c, "Permissions update failed") } h.logger.Info("User permissions updated successfully", map[string]interface{}{ "user_id": userID.String(), "permissions": req.Permissions, "updated_by": currentUser.ID.String(), }) return response.Success(c, nil, "Permissions updated successfully") } // Logout handles panel user logout // @Summary Panel user logout // @Description Logout panel user (client should remove tokens) // @Tags user-auth // @Produce json // @Security ApiKeyAuth // @Success 200 {object} response.APIResponse // @Router /api/admin/auth/logout [post] func (h *UserAuthHandler) Logout(c echo.Context) error { // In a stateless JWT implementation, logout is typically handled client-side // by removing the tokens from storage. However, you could implement token // blacklisting here if needed. user, ok := c.Get("user").(*domain.User) if ok { h.logger.Info("Panel user logged out", map[string]interface{}{ "user_id": user.ID.String(), "email": user.Email, }) } return response.Success(c, nil, "Logged out successfully") }