routes_users.go (already on main) calls settingsHandler.GetPreferences / UpdatePreferences and gdprExportHandler.ExportJSON, but the methods only existed in the working tree — main wouldn't compile, so deploy.yml's build-backend job was stuck on the same compile error every run. Bundles the WIP swagger annotation sweep across chat / marketplace / role / settings / gdpr / etc. handlers with the regenerated swagger.json, swagger.yaml, docs.go and openapi.yaml. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
317 lines
10 KiB
Go
317 lines
10 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"time"
|
|
|
|
apperrors "veza-backend-api/internal/errors"
|
|
"veza-backend-api/internal/models"
|
|
"veza-backend-api/internal/services"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// RoleServiceInterface defines the interface for role operations
|
|
// This allows for easier testing with mocks
|
|
type RoleServiceInterface interface {
|
|
GetRoles(ctx context.Context) ([]models.Role, error)
|
|
GetRole(ctx context.Context, roleID uuid.UUID) (*models.Role, error)
|
|
CreateRole(ctx context.Context, role *models.Role) error
|
|
UpdateRole(ctx context.Context, roleID uuid.UUID, updates *models.Role) error
|
|
DeleteRole(ctx context.Context, roleID uuid.UUID) error
|
|
AssignRoleToUser(ctx context.Context, userID uuid.UUID, roleID uuid.UUID, assignedBy uuid.UUID, expiresAt *time.Time) error
|
|
RevokeRoleFromUser(ctx context.Context, userID uuid.UUID, roleID uuid.UUID) error
|
|
GetUserRoles(ctx context.Context, userID uuid.UUID) ([]models.Role, error)
|
|
}
|
|
|
|
// RoleHandler gère les endpoints de gestion des rôles
|
|
type RoleHandler struct {
|
|
roleService RoleServiceInterface
|
|
commonHandler *CommonHandler
|
|
}
|
|
|
|
// NewRoleHandler crée un nouveau RoleHandler
|
|
func NewRoleHandler(roleService *services.RoleService, logger *zap.Logger) *RoleHandler {
|
|
return &RoleHandler{
|
|
roleService: roleService,
|
|
commonHandler: NewCommonHandler(logger),
|
|
}
|
|
}
|
|
|
|
// NewRoleHandlerWithInterface crée un nouveau RoleHandler avec une interface (pour les tests)
|
|
func NewRoleHandlerWithInterface(roleService RoleServiceInterface, logger *zap.Logger) *RoleHandler {
|
|
return &RoleHandler{
|
|
roleService: roleService,
|
|
commonHandler: NewCommonHandler(logger),
|
|
}
|
|
}
|
|
|
|
// GetRoles récupère tous les rôles
|
|
// BE-API-007: Implement roles management endpoints
|
|
// @Summary List all roles
|
|
// @Description Get a list of all available roles in the system.
|
|
// @Tags Admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Success 200 {array} models.Role
|
|
// @Failure 401 {object} handlers.APIResponse "Unauthorized"
|
|
// @Router /api/v1/roles [get]
|
|
func (h *RoleHandler) GetRoles(c *gin.Context) {
|
|
roles, err := h.roleService.GetRoles(c.Request.Context())
|
|
if err != nil {
|
|
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to get roles", err))
|
|
return
|
|
}
|
|
RespondSuccess(c, http.StatusOK, roles)
|
|
}
|
|
|
|
// GetRole récupère un rôle par ID
|
|
// BE-API-007: Implement roles management endpoints
|
|
// @Summary Get role by ID
|
|
// @Description Get detailed information about a specific role.
|
|
// @Tags Admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param id path string true "Role ID"
|
|
// @Success 200 {object} models.Role
|
|
// @Failure 401 {object} handlers.APIResponse "Unauthorized"
|
|
// @Failure 404 {object} handlers.APIResponse "Role not found"
|
|
// @Router /api/v1/roles/{id} [get]
|
|
func (h *RoleHandler) GetRole(c *gin.Context) {
|
|
roleIDStr := c.Param("id")
|
|
roleID, err := uuid.Parse(roleIDStr)
|
|
if err != nil {
|
|
RespondWithAppError(c, apperrors.NewValidationError("invalid role id"))
|
|
return
|
|
}
|
|
|
|
role, err := h.roleService.GetRole(c.Request.Context(), roleID)
|
|
if err != nil {
|
|
if err.Error() == "role not found" {
|
|
RespondWithAppError(c, apperrors.NewNotFoundError("role"))
|
|
} else {
|
|
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to get role", err))
|
|
}
|
|
return
|
|
}
|
|
RespondSuccess(c, http.StatusOK, role)
|
|
}
|
|
|
|
// CreateRole crée un nouveau rôle
|
|
// @Summary Create role
|
|
// @Description Create a new custom role.
|
|
// @Tags Admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param role body models.Role true "Role data"
|
|
// @Success 201 {object} object{role=models.Role}
|
|
// @Failure 401 {object} handlers.APIResponse "Unauthorized"
|
|
// @Router /api/v1/roles [post]
|
|
func (h *RoleHandler) CreateRole(c *gin.Context) {
|
|
var role models.Role
|
|
if appErr := h.commonHandler.BindAndValidateJSON(c, &role); appErr != nil {
|
|
RespondWithAppError(c, appErr)
|
|
return
|
|
}
|
|
|
|
if err := h.roleService.CreateRole(c.Request.Context(), &role); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.JSON(http.StatusCreated, gin.H{"role": role})
|
|
}
|
|
|
|
// UpdateRole met à jour un rôle
|
|
// @Summary Update role
|
|
// @Description Update an existing role's information.
|
|
// @Tags Admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param id path string true "Role ID"
|
|
// @Param role body models.Role true "Role updates"
|
|
// @Success 200 {object} object{message=string}
|
|
// @Failure 401 {object} handlers.APIResponse "Unauthorized"
|
|
// @Failure 404 {object} handlers.APIResponse "Role not found"
|
|
// @Router /api/v1/roles/{id} [put]
|
|
func (h *RoleHandler) UpdateRole(c *gin.Context) {
|
|
roleIDStr := c.Param("id")
|
|
roleID, err := uuid.Parse(roleIDStr)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid role id"})
|
|
return
|
|
}
|
|
|
|
var updates models.Role
|
|
if appErr := h.commonHandler.BindAndValidateJSON(c, &updates); appErr != nil {
|
|
RespondWithAppError(c, appErr)
|
|
return
|
|
}
|
|
|
|
if err := h.roleService.UpdateRole(c.Request.Context(), roleID, &updates); err != nil {
|
|
if err.Error() == "role not found or is system role" {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
|
} else {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
}
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"message": "role updated"})
|
|
}
|
|
|
|
// DeleteRole supprime un rôle
|
|
// @Summary Delete role
|
|
// @Description Permanently delete a role. System roles cannot be deleted.
|
|
// @Tags Admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param id path string true "Role ID"
|
|
// @Success 200 {object} object{message=string}
|
|
// @Failure 401 {object} handlers.APIResponse "Unauthorized"
|
|
// @Failure 400 {object} handlers.APIResponse "Cannot delete system role"
|
|
// @Router /api/v1/roles/{id} [delete]
|
|
func (h *RoleHandler) DeleteRole(c *gin.Context) {
|
|
roleIDStr := c.Param("id")
|
|
roleID, err := uuid.Parse(roleIDStr)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid role id"})
|
|
return
|
|
}
|
|
|
|
if err := h.roleService.DeleteRole(c.Request.Context(), roleID); err != nil {
|
|
if err.Error() == "role not found" || err.Error() == "cannot delete system role" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
} else {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
}
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"message": "role deleted"})
|
|
}
|
|
|
|
// AssignRole assigne un rôle à un utilisateur
|
|
// BE-API-007: Implement roles management endpoints
|
|
// Supports both :id and :userId parameters for flexibility
|
|
// @Summary Assign role to user
|
|
// @Description Assign a specific role to a user, optionally with an expiration date.
|
|
// @Tags Admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param userId path string true "User ID"
|
|
// @Param assignment body object{role_id=string,expires_at=string} true "Role assignment data"
|
|
// @Success 200 {object} object{message=string}
|
|
// @Failure 401 {object} handlers.APIResponse "Unauthorized"
|
|
// @Router /api/v1/users/{userId}/roles [post]
|
|
func (h *RoleHandler) AssignRole(c *gin.Context) {
|
|
// Try :userId first (from /users/:userId/roles), fallback to :id
|
|
userIDStr := c.Param("userId")
|
|
if userIDStr == "" {
|
|
userIDStr = c.Param("id")
|
|
}
|
|
userID, err := uuid.Parse(userIDStr)
|
|
if err != nil {
|
|
RespondWithAppError(c, apperrors.NewValidationError("invalid user id"))
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
RoleID uuid.UUID `json:"role_id" binding:"required"`
|
|
ExpiresAt *time.Time `json:"expires_at"`
|
|
}
|
|
if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
|
|
RespondWithAppError(c, appErr)
|
|
return
|
|
}
|
|
|
|
// Récupérer l'ID de l'utilisateur qui assigne depuis le contexte
|
|
assignedBy, ok := GetUserIDUUID(c)
|
|
if !ok {
|
|
return // Erreur déjà envoyée par GetUserIDUUID
|
|
}
|
|
|
|
if err := h.roleService.AssignRoleToUser(c.Request.Context(), userID, req.RoleID, assignedBy, req.ExpiresAt); err != nil {
|
|
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to assign role", err))
|
|
return
|
|
}
|
|
RespondSuccess(c, http.StatusOK, gin.H{"message": "role assigned"})
|
|
}
|
|
|
|
// RevokeRole révoque un rôle d'un utilisateur
|
|
// BE-API-007: Implement roles management endpoints
|
|
// Supports both :id and :userId parameters for flexibility
|
|
// @Summary Revoke role from user
|
|
// @Description Remove a specific role assignment from a user.
|
|
// @Tags Admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param userId path string true "User ID"
|
|
// @Param roleId path string true "Role ID"
|
|
// @Success 200 {object} object{message=string}
|
|
// @Failure 401 {object} handlers.APIResponse "Unauthorized"
|
|
// @Failure 404 {object} handlers.APIResponse "Role assignment not found"
|
|
// @Router /api/v1/users/{userId}/roles/{roleId} [delete]
|
|
func (h *RoleHandler) RevokeRole(c *gin.Context) {
|
|
// Try :userId first (from /users/:userId/roles/:roleId), fallback to :id
|
|
userIDStr := c.Param("userId")
|
|
if userIDStr == "" {
|
|
userIDStr = c.Param("id")
|
|
}
|
|
userID, err := uuid.Parse(userIDStr)
|
|
if err != nil {
|
|
RespondWithAppError(c, apperrors.NewValidationError("invalid user id"))
|
|
return
|
|
}
|
|
|
|
roleIDStr := c.Param("roleId")
|
|
roleID, err := uuid.Parse(roleIDStr)
|
|
if err != nil {
|
|
RespondWithAppError(c, apperrors.NewValidationError("invalid role id"))
|
|
return
|
|
}
|
|
|
|
if err := h.roleService.RevokeRoleFromUser(c.Request.Context(), userID, roleID); err != nil {
|
|
if err.Error() == "role assignment not found" {
|
|
RespondWithAppError(c, apperrors.NewNotFoundError("role assignment"))
|
|
} else {
|
|
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to revoke role", err))
|
|
}
|
|
return
|
|
}
|
|
RespondSuccess(c, http.StatusOK, gin.H{"message": "role revoked"})
|
|
}
|
|
|
|
// GetUserRoles récupère tous les rôles d'un utilisateur
|
|
// @Summary List user roles
|
|
// @Description Get all roles currently assigned to a specific user.
|
|
// @Tags Admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param id path string true "User ID"
|
|
// @Success 200 {object} object{roles=array}
|
|
// @Failure 401 {object} handlers.APIResponse "Unauthorized"
|
|
// @Router /api/v1/users/{id}/roles [get]
|
|
func (h *RoleHandler) GetUserRoles(c *gin.Context) {
|
|
userIDStr := c.Param("id")
|
|
userID, err := uuid.Parse(userIDStr)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid user id"})
|
|
return
|
|
}
|
|
|
|
roles, err := h.roleService.GetUserRoles(c.Request.Context(), userID)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"roles": roles})
|
|
}
|