169 lines
5.3 KiB
Go
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
|
|
}
|