- Created comprehensive sanitization utility functions - SanitizeInput, SanitizeText, SanitizeHTML, SanitizeURL, SanitizeEmail, SanitizeUsername - Applied sanitization to profile handler (username, bio, names, search) - Applied sanitization to social posts content - Applied sanitization to comment content - Applied sanitization to playlist titles and descriptions - All functions prevent XSS via HTML escaping and remove dangerous URL schemes - Removes control characters and limits input length to prevent DoS
157 lines
5.2 KiB
Go
157 lines
5.2 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"veza-backend-api/internal/core/social"
|
|
"veza-backend-api/internal/utils"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// SocialHandler gère les opérations sociales
|
|
type SocialHandler struct {
|
|
service social.SocialService
|
|
commonHandler *CommonHandler
|
|
}
|
|
|
|
// NewSocialHandler crée une nouvelle instance de SocialHandler
|
|
func NewSocialHandler(service social.SocialService, logger *zap.Logger) *SocialHandler {
|
|
return &SocialHandler{
|
|
service: service,
|
|
commonHandler: NewCommonHandler(logger),
|
|
}
|
|
}
|
|
|
|
// CreatePostRequest DTO pour la création de post
|
|
// GO-013: Validation améliorée avec tags go-validator
|
|
// MOD-P1-001: Ajout tags validate pour validation systématique
|
|
type CreatePostRequest struct {
|
|
Content string `json:"content" binding:"required,min=1,max=5000" validate:"required,min=1,max=5000"`
|
|
Attachments map[string]string `json:"attachments" validate:"omitempty"` // track_id, playlist_id (UUID strings)
|
|
}
|
|
|
|
// CreatePost crée un post
|
|
// GO-013: Utilise validator centralisé pour validation améliorée
|
|
// P0: JSON Hardening - Utilise BindAndValidateJSON pour une gestion robuste des erreurs
|
|
func (h *SocialHandler) CreatePost(c *gin.Context) {
|
|
userID, ok := GetUserIDUUID(c)
|
|
if !ok {
|
|
return // Erreur déjà envoyée par GetUserIDUUID
|
|
}
|
|
|
|
var req CreatePostRequest
|
|
if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
|
|
RespondWithAppError(c, appErr)
|
|
return
|
|
}
|
|
|
|
// BE-SEC-009: Sanitize user inputs to prevent XSS and injection attacks
|
|
req.Content = utils.SanitizeText(req.Content, 5000)
|
|
|
|
attachments := make(map[string]uuid.UUID)
|
|
for k, v := range req.Attachments {
|
|
if id, err := uuid.Parse(v); err == nil {
|
|
attachments[k] = id
|
|
}
|
|
}
|
|
|
|
post, err := h.service.CreatePost(c.Request.Context(), userID, req.Content, attachments)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create post"})
|
|
return
|
|
}
|
|
|
|
RespondSuccess(c, http.StatusCreated, post)
|
|
}
|
|
|
|
// ToggleLikeRequest DTO pour liker
|
|
// GO-013: Validation améliorée avec tags go-validator
|
|
// MOD-P1-001: Ajout tags validate pour validation systématique
|
|
type ToggleLikeRequest struct {
|
|
TargetID string `json:"target_id" binding:"required,uuid" validate:"required,uuid"`
|
|
TargetType string `json:"target_type" binding:"required,oneof=post track playlist" validate:"required,oneof=post track playlist"`
|
|
}
|
|
|
|
// ToggleLike like ou unlike un objet
|
|
// GO-013: Utilise validator centralisé pour validation améliorée
|
|
// P0: JSON Hardening - Utilise BindAndValidateJSON pour une gestion robuste des erreurs
|
|
func (h *SocialHandler) ToggleLike(c *gin.Context) {
|
|
userID, ok := GetUserIDUUID(c)
|
|
if !ok {
|
|
return // Erreur déjà envoyée par GetUserIDUUID
|
|
}
|
|
|
|
var req ToggleLikeRequest
|
|
if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
|
|
RespondWithAppError(c, appErr)
|
|
return
|
|
}
|
|
|
|
// UUID validation déjà fait par binding tag, mais on garde le parse pour compatibilité
|
|
targetID, err := uuid.Parse(req.TargetID)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid target_id format"})
|
|
return
|
|
}
|
|
|
|
liked, err := h.service.ToggleLike(c.Request.Context(), userID, targetID, req.TargetType)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to toggle like"})
|
|
return
|
|
}
|
|
|
|
RespondSuccess(c, http.StatusOK, gin.H{"liked": liked})
|
|
}
|
|
|
|
// AddCommentRequest DTO pour commenter
|
|
// GO-013: Validation améliorée avec tags go-validator
|
|
// MOD-P1-001: Ajout tags validate pour validation systématique
|
|
type AddCommentRequest struct {
|
|
TargetID string `json:"target_id" binding:"required,uuid" validate:"required,uuid"`
|
|
TargetType string `json:"target_type" binding:"required,oneof=post track playlist" validate:"required,oneof=post track playlist"`
|
|
Content string `json:"content" binding:"required,min=1,max=2000" validate:"required,min=1,max=2000"`
|
|
}
|
|
|
|
// AddComment ajoute un commentaire
|
|
// GO-013: Utilise validator centralisé pour validation améliorée
|
|
// P0: JSON Hardening - Utilise BindAndValidateJSON pour une gestion robuste des erreurs
|
|
func (h *SocialHandler) AddComment(c *gin.Context) {
|
|
userID, ok := GetUserIDUUID(c)
|
|
if !ok {
|
|
return // Erreur déjà envoyée par GetUserIDUUID
|
|
}
|
|
|
|
var req AddCommentRequest
|
|
if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
|
|
RespondWithAppError(c, appErr)
|
|
return
|
|
}
|
|
|
|
// UUID validation déjà fait par binding tag, mais on garde le parse pour compatibilité
|
|
targetID, err := uuid.Parse(req.TargetID)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid target_id format"})
|
|
return
|
|
}
|
|
|
|
comment, err := h.service.AddComment(c.Request.Context(), userID, targetID, req.TargetType, req.Content)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to add comment"})
|
|
return
|
|
}
|
|
|
|
RespondSuccess(c, http.StatusCreated, comment)
|
|
}
|
|
|
|
// GetFeed récupère le feed global
|
|
func (h *SocialHandler) GetFeed(c *gin.Context) {
|
|
feed, err := h.service.GetGlobalFeed(c.Request.Context(), 20, 0)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get feed"})
|
|
return
|
|
}
|
|
RespondSuccess(c, http.StatusOK, feed)
|
|
}
|