veza/veza-backend-api/internal/handlers/settings_handler.go
2026-03-06 19:13:16 +01:00

169 lines
5.3 KiB
Go

package handlers
import (
"fmt"
"net/http"
"time"
"veza-backend-api/internal/services"
"veza-backend-api/internal/types"
apperrors "veza-backend-api/internal/errors"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"go.uber.org/zap"
)
// UserServiceInterfaceForSettings defines the interface for user service operations needed by settings handler
// This allows for easier testing with mocks
type UserServiceInterfaceForSettings interface {
GetUserSettings(userID uuid.UUID) (*types.UserSettingsResponse, error)
UpdateUserSettings(userID uuid.UUID, req *types.UpdateSettingsRequest) error
}
// SettingsHandler handles settings-related operations
type SettingsHandler struct {
userService UserServiceInterfaceForSettings
commonHandler *CommonHandler
}
// NewSettingsHandler creates a new SettingsHandler instance
func NewSettingsHandler(userService *services.UserService, logger *zap.Logger) *SettingsHandler {
return &SettingsHandler{
userService: userService,
commonHandler: NewCommonHandler(logger),
}
}
// NewSettingsHandlerWithInterface creates a new SettingsHandler with an interface (for testing)
func NewSettingsHandlerWithInterface(userService UserServiceInterfaceForSettings, logger *zap.Logger) *SettingsHandler {
return &SettingsHandler{
userService: userService,
commonHandler: NewCommonHandler(logger),
}
}
// UserSettingsResponse represents the response structure for user settings
type UserSettingsResponse struct {
Notifications NotificationSettings `json:"notifications"`
Privacy PrivacySettings `json:"privacy"`
Content ContentSettings `json:"content"`
Preferences PreferenceSettings `json:"preferences"`
}
// NotificationSettings represents notification preferences
type NotificationSettings struct {
EmailNotifications bool `json:"email_notifications"`
PushNotifications bool `json:"push_notifications"`
BrowserNotifications bool `json:"browser_notifications"`
EmailOnFollow bool `json:"email_on_follow"`
EmailOnLike bool `json:"email_on_like"`
EmailOnComment bool `json:"email_on_comment"`
EmailOnMessage bool `json:"email_on_message"`
EmailOnMention bool `json:"email_on_mention"`
EmailMarketing bool `json:"email_marketing"`
}
// PrivacySettings represents privacy preferences
type PrivacySettings struct {
AllowSearchIndexing bool `json:"allow_search_indexing"`
ShowActivity bool `json:"show_activity"`
}
// ContentSettings represents content preferences
type ContentSettings struct {
ExplicitContent bool `json:"explicit_content"`
Autoplay bool `json:"autoplay"`
}
// PreferenceSettings represents user preferences
type PreferenceSettings struct {
Language string `json:"language"` // ISO 639-1
Timezone string `json:"timezone"`
Theme string `json:"theme"` // light, dark, auto
}
// GetSettings retrieves user settings
// T0231: Utilise l'utilisateur authentifié depuis le contexte (route /users/settings sans :id)
func (h *SettingsHandler) GetSettings(c *gin.Context) {
// Récupérer l'ID utilisateur depuis le contexte d'authentification
userID, ok := GetUserIDUUID(c)
if !ok {
return // Erreur déjà envoyée par GetUserIDUUID
}
if userID == uuid.Nil {
RespondWithAppError(c, apperrors.NewUnauthorizedError("user not authenticated"))
return
}
settings, err := h.userService.GetUserSettings(userID)
if err != nil {
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to get settings", err))
return
}
RespondSuccess(c, http.StatusOK, settings)
}
// UpdateSettings updates user settings
// T0232: Utilise l'utilisateur authentifié depuis le contexte (route /users/settings sans :id)
func (h *SettingsHandler) UpdateSettings(c *gin.Context) {
// Récupérer l'ID utilisateur depuis le contexte d'authentification
userID, ok := GetUserIDUUID(c)
if !ok {
return // Erreur déjà envoyée par GetUserIDUUID
}
if userID == uuid.Nil {
RespondWithAppError(c, apperrors.NewUnauthorizedError("user not authenticated"))
return
}
var req types.UpdateSettingsRequest
if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
RespondWithAppError(c, appErr)
return
}
// Valider preferences si fournies
if req.Preferences != nil {
if err := h.validatePreferences(req.Preferences); err != nil {
RespondWithAppError(c, apperrors.NewValidationError(err.Error()))
return
}
}
// Mettre à jour settings
if err := h.userService.UpdateUserSettings(userID, &req); err != nil {
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to update settings", err))
return
}
RespondSuccess(c, http.StatusOK, gin.H{"message": "settings updated"})
}
// validatePreferences validates preference settings
func (h *SettingsHandler) validatePreferences(prefs *types.PreferenceSettings) error {
// Valider language (ISO 639-1)
validLanguages := []string{"en", "fr", "es", "de", "it", "pt", "ru", "ja", "zh", "ko"}
if prefs.Language != "" {
valid := false
for _, lang := range validLanguages {
if prefs.Language == lang {
valid = true
break
}
}
if !valid {
return fmt.Errorf("invalid language code: %s", prefs.Language)
}
}
// Valider timezone (IANA timezone)
if prefs.Timezone != "" {
if _, err := time.LoadLocation(prefs.Timezone); err != nil {
return fmt.Errorf("invalid timezone: %s", prefs.Timezone)
}
}
return nil
}