turash/bugulma/backend/internal/handler/user_handler.go

336 lines
8.7 KiB
Go

package handler
import (
"net/http"
"strconv"
"bugulma/backend/internal/domain"
"bugulma/backend/internal/service"
"github.com/gin-gonic/gin"
)
type UserHandler struct {
userService *service.UserService
}
func NewUserHandler(userService *service.UserService) *UserHandler {
return &UserHandler{
userService: userService,
}
}
// ListUsers lists users with filters and pagination (admin only)
// @Summary List users
// @Tags admin
// @Produce json
// @Param role query string false "Filter by role"
// @Param isActive query bool false "Filter by active status"
// @Param search query string false "Search in email and name"
// @Param limit query int false "Limit" default(25)
// @Param offset query int false "Offset" default(0)
// @Success 200 {object} map[string]interface{}
// @Router /api/v1/admin/users [get]
func (h *UserHandler) ListUsers(c *gin.Context) {
filters := domain.UserListFilters{}
// Parse role filter
if roleStr := c.Query("role"); roleStr != "" {
role := domain.UserRole(roleStr)
filters.Role = &role
}
// Parse isActive filter
if isActiveStr := c.Query("isActive"); isActiveStr != "" {
isActive := isActiveStr == "true"
filters.IsActive = &isActive
}
// Parse search
filters.Search = c.Query("search")
// Parse pagination
limit := 25
offset := 0
if limitStr := c.Query("limit"); limitStr != "" {
if parsed, err := strconv.Atoi(limitStr); err == nil {
limit = parsed
}
}
if offsetStr := c.Query("offset"); offsetStr != "" {
if parsed, err := strconv.Atoi(offsetStr); err == nil {
offset = parsed
}
}
pagination := domain.PaginationParams{
Limit: limit,
Offset: offset,
}
result, err := h.userService.ListUsers(c.Request.Context(), filters, pagination)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"users": result.Items,
"total": result.Total,
"limit": limit,
"offset": offset,
})
}
// GetUser gets a user by ID (admin only)
// @Summary Get user
// @Tags admin
// @Produce json
// @Param id path string true "User ID"
// @Success 200 {object} domain.User
// @Router /api/v1/admin/users/:id [get]
func (h *UserHandler) GetUser(c *gin.Context) {
userID := c.Param("id")
user, err := h.userService.GetUser(c.Request.Context(), userID)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
return
}
// Don't return password
user.Password = ""
c.JSON(http.StatusOK, user)
}
// CreateUser creates a new user (admin only)
// @Summary Create user
// @Tags admin
// @Accept json
// @Produce json
// @Param request body CreateUserRequest true "User request"
// @Success 201 {object} domain.User
// @Router /api/v1/admin/users [post]
func (h *UserHandler) CreateUser(c *gin.Context) {
var req CreateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
createReq := service.CreateUserRequest{
Email: req.Email,
Name: req.Name,
Password: req.Password,
Role: domain.UserRole(req.Role),
Permissions: req.Permissions,
}
user, err := h.userService.CreateUser(c.Request.Context(), createReq)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Don't return password
user.Password = ""
c.JSON(http.StatusCreated, user)
}
type CreateUserRequest struct {
Email string `json:"email" binding:"required,email"`
Name string `json:"name" binding:"required"`
Password string `json:"password" binding:"required,min=8"`
Role string `json:"role" binding:"required"`
Permissions []string `json:"permissions"`
}
// UpdateUser updates a user (admin only)
// @Summary Update user
// @Tags admin
// @Accept json
// @Produce json
// @Param id path string true "User ID"
// @Param request body UpdateUserRequest true "Update request"
// @Success 200 {object} domain.User
// @Router /api/v1/admin/users/:id [put]
func (h *UserHandler) UpdateUser(c *gin.Context) {
userID := c.Param("id")
var req UpdateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
updateReq := service.UpdateUserRequest{}
if req.Name != nil {
updateReq.Name = req.Name
}
if req.Email != nil {
updateReq.Email = req.Email
}
if req.Role != nil {
role := domain.UserRole(*req.Role)
updateReq.Role = &role
}
if req.Permissions != nil {
updateReq.Permissions = req.Permissions
}
if req.IsActive != nil {
updateReq.IsActive = req.IsActive
}
user, err := h.userService.UpdateUser(c.Request.Context(), userID, updateReq)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Don't return password
user.Password = ""
c.JSON(http.StatusOK, user)
}
type UpdateUserRequest struct {
Name *string `json:"name"`
Email *string `json:"email"`
Role *string `json:"role"`
Permissions *[]string `json:"permissions"`
IsActive *bool `json:"isActive"`
}
// UpdateRole updates a user's role (admin only)
// @Summary Update user role
// @Tags admin
// @Accept json
// @Produce json
// @Param id path string true "User ID"
// @Param request body UpdateRoleRequest true "Role request"
// @Success 200 {object} map[string]string
// @Router /api/v1/admin/users/:id/role [patch]
func (h *UserHandler) UpdateRole(c *gin.Context) {
userID := c.Param("id")
var req UpdateRoleRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := h.userService.UpdateRole(c.Request.Context(), userID, domain.UserRole(req.Role)); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Role updated"})
}
type UpdateRoleRequest struct {
Role string `json:"role" binding:"required"`
}
// UpdatePermissions updates a user's permissions (admin only)
// @Summary Update user permissions
// @Tags admin
// @Accept json
// @Produce json
// @Param id path string true "User ID"
// @Param request body UpdatePermissionsRequest true "Permissions request"
// @Success 200 {object} map[string]string
// @Router /api/v1/admin/users/:id/permissions [patch]
func (h *UserHandler) UpdatePermissions(c *gin.Context) {
userID := c.Param("id")
var req UpdatePermissionsRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := h.userService.UpdatePermissions(c.Request.Context(), userID, req.Permissions); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Permissions updated"})
}
type UpdatePermissionsRequest struct {
Permissions []string `json:"permissions" binding:"required"`
}
// DeactivateUser deactivates a user (admin only)
// @Summary Deactivate user
// @Tags admin
// @Success 200 {object} map[string]string
// @Router /api/v1/admin/users/:id [delete]
func (h *UserHandler) DeactivateUser(c *gin.Context) {
userID := c.Param("id")
if err := h.userService.DeactivateUser(c.Request.Context(), userID); err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "User deactivated"})
}
// GetUserActivity gets activity log for a user (admin only)
// @Summary Get user activity
// @Tags admin
// @Produce json
// @Param id path string true "User ID"
// @Param limit query int false "Limit" default(50)
// @Param offset query int false "Offset" default(0)
// @Success 200 {object} map[string]interface{}
// @Router /api/v1/admin/users/:id/activity [get]
func (h *UserHandler) GetUserActivity(c *gin.Context) {
userID := c.Param("id")
limit := 50
offset := 0
if limitStr := c.Query("limit"); limitStr != "" {
if parsed, err := strconv.Atoi(limitStr); err == nil {
limit = parsed
}
}
if offsetStr := c.Query("offset"); offsetStr != "" {
if parsed, err := strconv.Atoi(offsetStr); err == nil {
offset = parsed
}
}
activities, total, err := h.userService.GetUserActivity(c.Request.Context(), userID, limit, offset)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"activities": activities,
"total": total,
"limit": limit,
"offset": offset,
})
}
// GetUserStats gets user statistics (admin only)
// @Summary Get user statistics
// @Tags admin
// @Produce json
// @Success 200 {object} map[string]interface{}
// @Router /api/v1/admin/users/stats [get]
func (h *UserHandler) GetUserStats(c *gin.Context) {
stats, err := h.userService.GetUserStats(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, stats)
}