// backend/notification_preferences.go package main import ( "context" "errors" "net/http" "strings" "github.com/jackc/pgx/v5" ) type notificationPreferencesResponse struct { InAppEnabled bool `json:"inAppEnabled"` SoundEnabled bool `json:"soundEnabled"` SoundName string `json:"soundName"` EmailEnabled bool `json:"emailEnabled"` OperationUpdates bool `json:"operationUpdates"` OperationJournals bool `json:"operationJournals"` DeviceUpdates bool `json:"deviceUpdates"` DeviceJournals bool `json:"deviceJournals"` SystemMessages bool `json:"systemMessages"` } func defaultNotificationPreferences() notificationPreferencesResponse { return notificationPreferencesResponse{ InAppEnabled: true, SoundEnabled: false, SoundName: "default", EmailEnabled: false, OperationUpdates: true, OperationJournals: true, DeviceUpdates: true, DeviceJournals: true, SystemMessages: true, } } func normalizeNotificationSoundName(value string) string { value = strings.TrimSpace(strings.ToLower(value)) switch value { case "default", "chime", "soft", "alert", "success": return value default: return "default" } } func (s *Server) handleGetNotificationPreferences(w http.ResponseWriter, r *http.Request) { user, ok := userFromContext(r.Context()) if !ok { writeError(w, http.StatusUnauthorized, "Unauthorized") return } settings, err := s.getNotificationPreferences(r.Context(), user.ID) if errors.Is(err, pgx.ErrNoRows) { writeJSON(w, http.StatusOK, map[string]any{ "settings": defaultNotificationPreferences(), }) return } if err != nil { writeError(w, http.StatusInternalServerError, "Benachrichtigungseinstellungen konnten nicht geladen werden") return } writeJSON(w, http.StatusOK, map[string]any{ "settings": settings, }) } func (s *Server) handleUpdateNotificationPreferences(w http.ResponseWriter, r *http.Request) { user, ok := userFromContext(r.Context()) if !ok { writeError(w, http.StatusUnauthorized, "Unauthorized") return } var input notificationPreferencesResponse if err := readJSON(r, &input); err != nil { writeError(w, http.StatusBadRequest, "Invalid JSON") return } input.SoundName = normalizeNotificationSoundName(input.SoundName) _, err := s.db.Exec( r.Context(), ` INSERT INTO notification_preferences ( user_id, in_app_enabled, sound_enabled, sound_name, email_enabled, operation_updates, operation_journals, device_updates, device_journals, system_messages ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) ON CONFLICT (user_id) DO UPDATE SET in_app_enabled = EXCLUDED.in_app_enabled, sound_enabled = EXCLUDED.sound_enabled, sound_name = EXCLUDED.sound_name, email_enabled = EXCLUDED.email_enabled, operation_updates = EXCLUDED.operation_updates, operation_journals = EXCLUDED.operation_journals, device_updates = EXCLUDED.device_updates, device_journals = EXCLUDED.device_journals, system_messages = EXCLUDED.system_messages, updated_at = now() `, user.ID, input.InAppEnabled, input.SoundEnabled, input.SoundName, input.EmailEnabled, input.OperationUpdates, input.OperationJournals, input.DeviceUpdates, input.DeviceJournals, input.SystemMessages, ) if err != nil { writeError(w, http.StatusInternalServerError, "Benachrichtigungseinstellungen konnten nicht gespeichert werden") return } settings, err := s.getNotificationPreferences(r.Context(), user.ID) if err != nil { writeError(w, http.StatusInternalServerError, "Benachrichtigungseinstellungen wurden gespeichert, konnten aber nicht neu geladen werden") return } writeJSON(w, http.StatusOK, map[string]any{ "settings": settings, }) } func (s *Server) getNotificationPreferences(ctx context.Context, userID string) (notificationPreferencesResponse, error) { settings := defaultNotificationPreferences() err := s.db.QueryRow( ctx, ` SELECT in_app_enabled, sound_enabled, COALESCE(sound_name, 'default'), email_enabled, operation_updates, operation_journals, device_updates, device_journals, system_messages FROM notification_preferences WHERE user_id = $1 LIMIT 1 `, userID, ).Scan( &settings.InAppEnabled, &settings.SoundEnabled, &settings.SoundName, &settings.EmailEnabled, &settings.OperationUpdates, &settings.OperationJournals, &settings.DeviceUpdates, &settings.DeviceJournals, &settings.SystemMessages, ) settings.SoundName = normalizeNotificationSoundName(settings.SoundName) return settings, err } func (s *Server) getNotificationPreferencesOrDefault(ctx context.Context, userID string) (notificationPreferencesResponse, error) { settings, err := s.getNotificationPreferences(ctx, userID) if errors.Is(err, pgx.ErrNoRows) { return defaultNotificationPreferences(), nil } return settings, err }