veza/veza-backend-api/internal/handlers/settings_handler.go
senke ef386e0ae3 fix(backend): commit swagger annotation pass + missing handler methods
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>
2026-05-01 10:16:57 +02:00

254 lines
8.4 KiB
Go

package handlers
import (
"fmt"
"net/http"
"time"
apperrors "veza-backend-api/internal/errors"
"veza-backend-api/internal/services"
"veza-backend-api/internal/types"
"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)
// @Summary Get User Settings
// @Description Get settings for the authenticated user
// @Tags User
// @Accept json
// @Produce json
// @Success 200 {object} handlers.APIResponse{data=handlers.UserSettingsResponse}
// @Failure 401 {object} handlers.APIResponse "Unauthorized"
// @Failure 500 {object} handlers.APIResponse "Internal Error"
// @Router /users/settings [get]
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)
// @Summary Update User Settings
// @Description Update settings for the authenticated user
// @Tags User
// @Accept json
// @Produce json
// @Param settings body types.UpdateSettingsRequest true "Settings Data"
// @Success 200 {object} handlers.APIResponse{data=object{message=string}}
// @Failure 400 {object} handlers.APIResponse "Validation Error"
// @Failure 401 {object} handlers.APIResponse "Unauthorized"
// @Failure 500 {object} handlers.APIResponse "Internal Error"
// @Router /users/settings [put]
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
}
// GetPreferences récupère les préférences d'affichage (alias pour settings.preferences)
// @Summary Get User Preferences
// @Description Get UI/Appearance preferences for the authenticated user
// @Tags User
// @Accept json
// @Produce json
// @Success 200 {object} handlers.APIResponse{data=types.PreferenceSettings}
// @Failure 401 {object} handlers.APIResponse "Unauthorized"
// @Failure 500 {object} handlers.APIResponse "Internal Error"
// @Router /users/me/preferences [get]
func (h *SettingsHandler) GetPreferences(c *gin.Context) {
userID, ok := GetUserIDUUID(c)
if !ok {
return
}
settings, err := h.userService.GetUserSettings(userID)
if err != nil {
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to get preferences", err))
return
}
RespondSuccess(c, http.StatusOK, settings.Preferences)
}
// UpdatePreferences met à jour les préférences d'affichage
// @Summary Update User Preferences
// @Description Update UI/Appearance preferences (theme, contrast, etc.)
// @Tags User
// @Accept json
// @Produce json
// @Param preferences body types.PreferenceSettings true "Preferences Data"
// @Success 200 {object} handlers.APIResponse{data=types.PreferenceSettings}
// @Failure 400 {object} handlers.APIResponse "Validation Error"
// @Failure 401 {object} handlers.APIResponse "Unauthorized"
// @Router /users/me/preferences [put]
func (h *SettingsHandler) UpdatePreferences(c *gin.Context) {
userID, ok := GetUserIDUUID(c)
if !ok {
return
}
var prefs types.PreferenceSettings
if appErr := h.commonHandler.BindAndValidateJSON(c, &prefs); appErr != nil {
RespondWithAppError(c, appErr)
return
}
if err := h.validatePreferences(&prefs); err != nil {
RespondWithAppError(c, apperrors.NewValidationError(err.Error()))
return
}
req := types.UpdateSettingsRequest{
Preferences: &prefs,
}
if err := h.userService.UpdateUserSettings(userID, &req); err != nil {
RespondWithAppError(c, apperrors.NewInternalErrorWrap("failed to update preferences", err))
return
}
RespondSuccess(c, http.StatusOK, prefs)
}