diff --git a/cmd/soundtouch-service/main.go b/cmd/soundtouch-service/main.go index 65a8d3a..1d822ae 100644 --- a/cmd/soundtouch-service/main.go +++ b/cmd/soundtouch-service/main.go @@ -189,6 +189,11 @@ func main() { Usage: "Endpoints to mirror to Bose Cloud (comma-separated or multiple flags)", EnvVars: []string{"MIRROR_ENDPOINTS"}, }, + &cli.StringSliceFlag{ + Name: "skip-mirror-endpoints", + Usage: "Endpoints to skip mirroring to Bose Cloud (comma-separated or multiple flags)", + EnvVars: []string{"SKIP_MIRROR_ENDPOINTS"}, + }, &cli.StringSliceFlag{ Name: "internal-paths", Usage: "Paths for internal requests (comma-separated or multiple flags)", @@ -241,7 +246,7 @@ func main() { server.SetVersionInfo(version, commit, date) server.SetDiscoverySettings(config.discoveryInterval, persisted.DiscoveryEnabled) server.SetDNSSettings(persisted.DNSEnabled, strings.Join(persisted.DNSUpstream, ","), persisted.DNSBindAddr) - server.SetMirrorSettings(persisted.MirrorEnabled, persisted.MirrorEndpoints, persisted.PreferredSource) + server.SetMirrorSettings(persisted.MirrorEnabled, persisted.MirrorEndpoints, persisted.SkipMirrorEndpoints, persisted.PreferredSource) server.SetInternalPaths(persisted.InternalPaths) server.SetSpotifyConfig(config.spotifyClientID, config.spotifyClientSecret, config.spotifyRedirectURI) server.SetMgmtConfig(config.mgmtUsername, config.mgmtPassword) @@ -374,6 +379,7 @@ type serviceConfig struct { dnsBind string mirrorEnabled bool mirrorEndpoints []string + skipMirrorEndpoints []string internalPaths []string discoveryInterval time.Duration domains []string @@ -448,6 +454,7 @@ func loadConfig(c *cli.Context) serviceConfig { mgmtPassword := c.String("mgmt-password") mirrorEnabled := c.Bool("mirror-enabled") mirrorEndpoints := c.StringSlice("mirror-endpoints") + skipMirrorEndpoints := c.StringSlice("skip-mirror-endpoints") internalPaths := c.StringSlice("internal-paths") migrationEnabled := c.Bool("migration-enabled") migrationDryRun := c.Bool("migration-dry-run") @@ -469,6 +476,7 @@ func loadConfig(c *cli.Context) serviceConfig { dnsBind: dnsBind, mirrorEnabled: mirrorEnabled, mirrorEndpoints: mirrorEndpoints, + skipMirrorEndpoints: skipMirrorEndpoints, internalPaths: internalPaths, discoveryInterval: discoveryInterval, domains: domains, @@ -565,6 +573,7 @@ func applyPersistedSettings(ds *datastore.DataStore, config *serviceConfig) data config.mirrorEnabled = persisted.MirrorEnabled config.mirrorEndpoints = persisted.MirrorEndpoints + config.skipMirrorEndpoints = persisted.SkipMirrorEndpoints config.preferredSource = persisted.PreferredSource config.internalPaths = persisted.InternalPaths @@ -573,20 +582,21 @@ func applyPersistedSettings(ds *datastore.DataStore, config *serviceConfig) data func createDefaultSettings(ds *datastore.DataStore, config serviceConfig) datastore.Settings { settings := datastore.Settings{ - ServerURL: config.serverURL, - HTTPServerURL: config.httpsServerURL, - RedactLogs: config.redact, - LogBodies: config.logBody, - RecordInteractions: config.record, - DiscoveryInterval: config.discoveryInterval.String(), - DiscoveryEnabled: true, - DNSEnabled: config.dnsEnabled, - DNSUpstream: strings.Split(config.dnsUpstream, ","), - DNSBindAddr: config.dnsBind, - MirrorEnabled: config.mirrorEnabled, - MirrorEndpoints: config.mirrorEndpoints, - PreferredSource: config.preferredSource, - InternalPaths: config.internalPaths, + ServerURL: config.serverURL, + HTTPServerURL: config.httpsServerURL, + RedactLogs: config.redact, + LogBodies: config.logBody, + RecordInteractions: config.record, + DiscoveryInterval: config.discoveryInterval.String(), + DiscoveryEnabled: true, + DNSEnabled: config.dnsEnabled, + DNSUpstream: strings.Split(config.dnsUpstream, ","), + DNSBindAddr: config.dnsBind, + MirrorEnabled: config.mirrorEnabled, + MirrorEndpoints: config.mirrorEndpoints, + SkipMirrorEndpoints: config.skipMirrorEndpoints, + PreferredSource: config.preferredSource, + InternalPaths: config.internalPaths, Shortcuts: map[string]int{ "/.well-known/appspecific/com.chrome.devtools.json": http.StatusNotFound, "/sw.js": http.StatusNotFound, diff --git a/pkg/service/datastore/datastore.go b/pkg/service/datastore/datastore.go index 6e310bc..7ca259a 100644 --- a/pkg/service/datastore/datastore.go +++ b/pkg/service/datastore/datastore.go @@ -1138,21 +1138,22 @@ func (ds *DataStore) GetETagForAccount(account, device string) int64 { // Settings represents the global service settings. type Settings struct { - ServerURL string `json:"server_url"` - HTTPServerURL string `json:"https_server_url,omitempty"` - RedactLogs bool `json:"redact_logs"` - LogBodies bool `json:"log_bodies"` - RecordInteractions bool `json:"record_interactions"` - DiscoveryInterval string `json:"discovery_interval,omitempty"` - DiscoveryEnabled bool `json:"discovery_enabled"` - DNSEnabled bool `json:"dns_enabled"` - DNSUpstream []string `json:"dns_upstream,omitempty"` - DNSBindAddr string `json:"dns_bind_addr,omitempty"` - MirrorEnabled bool `json:"mirror_enabled"` - MirrorEndpoints []string `json:"mirror_endpoints,omitempty"` - PreferredSource string `json:"preferred_source,omitempty"` - InternalPaths []string `json:"internal_paths,omitempty"` - Shortcuts map[string]int `json:"shortcuts,omitempty"` + ServerURL string `json:"server_url"` + HTTPServerURL string `json:"https_server_url,omitempty"` + RedactLogs bool `json:"redact_logs"` + LogBodies bool `json:"log_bodies"` + RecordInteractions bool `json:"record_interactions"` + DiscoveryInterval string `json:"discovery_interval,omitempty"` + DiscoveryEnabled bool `json:"discovery_enabled"` + DNSEnabled bool `json:"dns_enabled"` + DNSUpstream []string `json:"dns_upstream,omitempty"` + DNSBindAddr string `json:"dns_bind_addr,omitempty"` + MirrorEnabled bool `json:"mirror_enabled"` + MirrorEndpoints []string `json:"mirror_endpoints,omitempty"` + SkipMirrorEndpoints []string `json:"skip_mirror_endpoints,omitempty"` + PreferredSource string `json:"preferred_source,omitempty"` + InternalPaths []string `json:"internal_paths,omitempty"` + Shortcuts map[string]int `json:"shortcuts,omitempty"` } // GetSettings retrieves the global service settings. diff --git a/pkg/service/handlers/handlers_oauth_test.go b/pkg/service/handlers/handlers_oauth_test.go index 98ae544..47cc7b0 100644 --- a/pkg/service/handlers/handlers_oauth_test.go +++ b/pkg/service/handlers/handlers_oauth_test.go @@ -81,7 +81,7 @@ func TestHandleBoseSpotifyToken_FallbackToProxy(t *testing.T) { // Mirroring must be enabled for HandleBoseProxy to work (based on previous changes) // Actually I reverted that, so it should work regardless of MirrorEnabled now. - server.SetMirrorSettings(true, nil, "") + server.SetMirrorSettings(true, nil, nil, "") // chi.URLParam works when using chi router r := chi.NewRouter() diff --git a/pkg/service/handlers/handlers_setup.go b/pkg/service/handlers/handlers_setup.go index 400063c..74d5990 100644 --- a/pkg/service/handlers/handlers_setup.go +++ b/pkg/service/handlers/handlers_setup.go @@ -156,6 +156,7 @@ func (s *Server) HandleGetSettings(w http.ResponseWriter, _ *http.Request) { dnsBindAddr := s.dnsBindAddr mirrorEnabled := s.mirrorEnabled mirrorEndpoints := s.mirrorEndpoints + skipMirrorEndpoints := s.skipMirrorEndpoints preferredSource := s.preferredSource internalPaths := s.internalPaths redact, logBody, record := s.proxyRedact, s.proxyLogBody, s.recordEnabled @@ -166,24 +167,25 @@ func (s *Server) HandleGetSettings(w http.ResponseWriter, _ *http.Request) { dnsRunning, actualBind := s.GetDNSRunning() if err := json.NewEncoder(w).Encode(map[string]interface{}{ - "server_url": serverURL, - "https_server_url": httpsServerURL, - "discovery_interval": discoveryInterval, - "discovery_enabled": discoveryEnabled, - "dns_enabled": dnsEnabled, - "dns_running": dnsRunning, - "dns_actual_bind": actualBind, - "dns_upstream": strings.Join(dnsUpstream, ","), - "dns_bind_addr": dnsBindAddr, - "mirror_enabled": mirrorEnabled, - "mirror_endpoints": mirrorEndpoints, - "preferred_source": preferredSource, - "internal_paths": internalPaths, - "redact_logs": redact, - "log_bodies": logBody, - "record_interactions": record, - "shortcuts": shortcuts, - "spotify_configured": spotifyConfigured, + "server_url": serverURL, + "https_server_url": httpsServerURL, + "discovery_interval": discoveryInterval, + "discovery_enabled": discoveryEnabled, + "dns_enabled": dnsEnabled, + "dns_running": dnsRunning, + "dns_actual_bind": actualBind, + "dns_upstream": strings.Join(dnsUpstream, ","), + "dns_bind_addr": dnsBindAddr, + "mirror_enabled": mirrorEnabled, + "mirror_endpoints": mirrorEndpoints, + "skip_mirror_endpoints": skipMirrorEndpoints, + "preferred_source": preferredSource, + "internal_paths": internalPaths, + "redact_logs": redact, + "log_bodies": logBody, + "record_interactions": record, + "shortcuts": shortcuts, + "spotify_configured": spotifyConfigured, }); err != nil { http.Error(w, "Failed to encode response", http.StatusInternalServerError) return @@ -193,17 +195,18 @@ func (s *Server) HandleGetSettings(w http.ResponseWriter, _ *http.Request) { // HandleUpdateSettings updates the service settings. func (s *Server) HandleUpdateSettings(w http.ResponseWriter, r *http.Request) { var settings struct { - ServerURL string `json:"server_url"` - DiscoveryInterval string `json:"discovery_interval"` - DiscoveryEnabled bool `json:"discovery_enabled"` - DNSEnabled bool `json:"dns_enabled"` - DNSUpstream string `json:"dns_upstream"` - DNSBindAddr string `json:"dns_bind_addr"` - MirrorEnabled bool `json:"mirror_enabled"` - MirrorEndpoints []string `json:"mirror_endpoints"` - PreferredSource string `json:"preferred_source"` - InternalPaths []string `json:"internal_paths"` - Shortcuts map[string]int `json:"shortcuts"` + ServerURL string `json:"server_url"` + DiscoveryInterval string `json:"discovery_interval"` + DiscoveryEnabled bool `json:"discovery_enabled"` + DNSEnabled bool `json:"dns_enabled"` + DNSUpstream string `json:"dns_upstream"` + DNSBindAddr string `json:"dns_bind_addr"` + MirrorEnabled bool `json:"mirror_enabled"` + MirrorEndpoints []string `json:"mirror_endpoints"` + SkipMirrorEndpoints []string `json:"skip_mirror_endpoints"` + PreferredSource string `json:"preferred_source"` + InternalPaths []string `json:"internal_paths"` + Shortcuts map[string]int `json:"shortcuts"` } if err := json.NewDecoder(r.Body).Decode(&settings); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) @@ -249,6 +252,7 @@ func (s *Server) HandleUpdateSettings(w http.ResponseWriter, r *http.Request) { s.mirrorEnabled = settings.MirrorEnabled s.mirrorEndpoints = settings.MirrorEndpoints + s.skipMirrorEndpoints = settings.SkipMirrorEndpoints s.preferredSource = settings.PreferredSource s.internalPaths = settings.InternalPaths @@ -269,21 +273,22 @@ func (s *Server) HandleUpdateSettings(w http.ResponseWriter, r *http.Request) { log.Printf("Saving updated settings to %s/settings.json", s.ds.DataDir) err = s.ds.SaveSettings(datastore.Settings{ - ServerURL: s.serverURL, - HTTPServerURL: currentHTTPS, - RedactLogs: currentRedact, - LogBodies: currentLogBody, - RecordInteractions: currentRecord, - DiscoveryInterval: s.discoveryInterval.String(), - DiscoveryEnabled: s.discoveryEnabled, - DNSEnabled: s.dnsEnabled, - DNSUpstream: s.dnsUpstream, - DNSBindAddr: s.dnsBindAddr, - MirrorEnabled: s.mirrorEnabled, - MirrorEndpoints: s.mirrorEndpoints, - PreferredSource: s.preferredSource, - InternalPaths: s.internalPaths, - Shortcuts: s.shortcuts, + ServerURL: s.serverURL, + HTTPServerURL: currentHTTPS, + RedactLogs: currentRedact, + LogBodies: currentLogBody, + RecordInteractions: currentRecord, + DiscoveryInterval: s.discoveryInterval.String(), + DiscoveryEnabled: s.discoveryEnabled, + DNSEnabled: s.dnsEnabled, + DNSUpstream: s.dnsUpstream, + DNSBindAddr: s.dnsBindAddr, + MirrorEnabled: s.mirrorEnabled, + MirrorEndpoints: s.mirrorEndpoints, + SkipMirrorEndpoints: s.skipMirrorEndpoints, + PreferredSource: s.preferredSource, + InternalPaths: s.internalPaths, + Shortcuts: s.shortcuts, }) dnsEnabled := s.dnsEnabled diff --git a/pkg/service/handlers/mirror_loop_prevention_test.go b/pkg/service/handlers/mirror_loop_prevention_test.go index 78e6fa8..025febd 100644 --- a/pkg/service/handlers/mirror_loop_prevention_test.go +++ b/pkg/service/handlers/mirror_loop_prevention_test.go @@ -20,7 +20,7 @@ func TestMirrorMiddleware_InfiniteLoop(t *testing.T) { _ = ds.Initialize() server := NewServer(ds, nil, "http://localhost:8000", false, false, false) - server.SetMirrorSettings(true, []string{"/loop"}, "upstream") + server.SetMirrorSettings(true, []string{"/loop"}, nil, "upstream") // Create a handler that would be the "next" in the chain. // If the loop occurs, this will be called repeatedly. diff --git a/pkg/service/handlers/mirror_middleware.go b/pkg/service/handlers/mirror_middleware.go index c22b285..f0f93b0 100644 --- a/pkg/service/handlers/mirror_middleware.go +++ b/pkg/service/handlers/mirror_middleware.go @@ -26,10 +26,10 @@ import ( // MirrorMiddleware returns a middleware that mirrors specific requests to the Bose upstream. func (s *Server) MirrorMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - enabled, endpoints, preferredSource := s.getMirrorSettings() + enabled, endpoints, skipEndpoints, preferredSource := s.getMirrorSettings() isMirrorRequest := r.Header.Get("X-Mirror-Request") == "true" - if !enabled || isMirrorRequest || len(endpoints) == 0 || !s.shouldMirror(r.URL.Path, endpoints) { + if !enabled || isMirrorRequest || len(endpoints) == 0 || !s.shouldMirror(r.URL.Path, endpoints) || s.shouldSkipMirror(r.URL.Path, skipEndpoints) { next.ServeHTTP(w, r) return } @@ -64,11 +64,11 @@ func (s *Server) MirrorMiddleware(next http.Handler) http.Handler { }) } -func (s *Server) getMirrorSettings() (bool, []string, string) { +func (s *Server) getMirrorSettings() (bool, []string, []string, string) { s.mu.RLock() defer s.mu.RUnlock() - return s.mirrorEnabled, s.mirrorEndpoints, s.preferredSource + return s.mirrorEnabled, s.mirrorEndpoints, s.skipMirrorEndpoints, s.preferredSource } func (s *Server) shouldMirror(path string, endpoints []string) bool { @@ -81,6 +81,16 @@ func (s *Server) shouldMirror(path string, endpoints []string) bool { return false } +func (s *Server) shouldSkipMirror(path string, skipEndpoints []string) bool { + for _, pattern := range skipEndpoints { + if matchPattern(pattern, path) { + return true + } + } + + return false +} + func (s *Server) mirrorUpstreamPreferred(detachedCtx context.Context, w http.ResponseWriter, r *http.Request, next http.Handler, bodyBytes []byte) { log.Printf("[MIRROR] Upstream is preferred source for %s %s", r.Method, r.URL.Path) diff --git a/pkg/service/handlers/mirror_preferred_test.go b/pkg/service/handlers/mirror_preferred_test.go index c5ea28a..47939eb 100644 --- a/pkg/service/handlers/mirror_preferred_test.go +++ b/pkg/service/handlers/mirror_preferred_test.go @@ -40,7 +40,7 @@ func TestMirrorMiddleware_PreferredSource(t *testing.T) { // 3. Setup our server with MirrorMiddleware server := NewServer(ds, nil, "http://localhost:8000", false, false, false) - server.SetMirrorSettings(true, []string{"/test/local"}, "local") + server.SetMirrorSettings(true, []string{"/test/local"}, nil, "local") // We need to trick performMirror to use our mock upstream. // performMirror uses r.Host. @@ -50,7 +50,7 @@ func TestMirrorMiddleware_PreferredSource(t *testing.T) { middleware := server.MirrorMiddleware(r) t.Run("PreferredLocal", func(t *testing.T) { - server.SetMirrorSettings(true, []string{"/test/local"}, "local") + server.SetMirrorSettings(true, []string{"/test/local"}, nil, "local") req := httptest.NewRequest("GET", "/test/local", nil) req.Host = upstreamHost // So performMirror targets the mock upstream @@ -70,7 +70,7 @@ func TestMirrorMiddleware_PreferredSource(t *testing.T) { }) t.Run("PreferredUpstream", func(t *testing.T) { - server.SetMirrorSettings(true, []string{"/test/local"}, "upstream") + server.SetMirrorSettings(true, []string{"/test/local"}, nil, "upstream") req := httptest.NewRequest("GET", "/test/local", nil) req.Host = upstreamHost @@ -90,7 +90,7 @@ func TestMirrorMiddleware_PreferredSource(t *testing.T) { }) t.Run("FallbackToLocal", func(t *testing.T) { - server.SetMirrorSettings(true, []string{"/test/local"}, "upstream") + server.SetMirrorSettings(true, []string{"/test/local"}, nil, "upstream") // Use a non-existent host for mirror to trigger failure req := httptest.NewRequest("GET", "/test/local", nil) diff --git a/pkg/service/handlers/mirror_test.go b/pkg/service/handlers/mirror_test.go index 8a33d53..6a687b4 100644 --- a/pkg/service/handlers/mirror_test.go +++ b/pkg/service/handlers/mirror_test.go @@ -45,7 +45,7 @@ func TestMirroring(t *testing.T) { recorder := proxy.NewRecorder(tempDir) server.SetRecorder(recorder) server.SetRecordEnabled(true) - server.SetMirrorSettings(true, []string{"/streaming/account/*/device/*/recent"}, "local") + server.SetMirrorSettings(true, []string{"/streaming/account/*/device/*/recent"}, nil, "local") ts := httptest.NewServer(r) defer ts.Close() @@ -167,7 +167,7 @@ func TestMirroring(t *testing.T) { defer postUpstream.Close() // Setup mirroring for the POST endpoint - server.SetMirrorSettings(true, []string{"/v1/scmudc/*"}, "local") + server.SetMirrorSettings(true, []string{"/v1/scmudc/*"}, nil, "local") requestBody := `{"envelope":{"monoTime":234906,"payloadProtocolVersion":"3.1","payloadType":"scmudc","protocolVersion":"1.0","time":"2026-02-25T23:03:14.976349+00:00","uniqueId":"A81B6A536A98"},"payload":{"deviceInfo":{"boseID":"3230304","deviceID":"A81B6A536A98","deviceType":"SoundTouch 10","serialNumber":"I6332527703739342000020","softwareVersion":"27.0.6.46330.5043500 epdbuild.trunk.hepdswbld04.2022-08-04T11:20:29","systemSerialNumber":"069231P63364828AE"},"events":[{"data":{"play-state":"PAUSE_STATE"},"monoTime":234904,"time":"2026-02-25T23:03:14.973466+00:00","type":"play-state-changed"}]}}` diff --git a/pkg/service/handlers/server.go b/pkg/service/handlers/server.go index 61b6ef1..fd081ea 100644 --- a/pkg/service/handlers/server.go +++ b/pkg/service/handlers/server.go @@ -40,6 +40,7 @@ type Server struct { dnsBindAddr string mirrorEnabled bool mirrorEndpoints []string + skipMirrorEndpoints []string preferredSource string internalPaths []string shortcuts map[string]int @@ -319,12 +320,13 @@ func (s *Server) SetMgmtConfig(username, password string) { } // SetMirrorSettings sets the mirroring settings for the server. -func (s *Server) SetMirrorSettings(enabled bool, endpoints []string, preferredSource string) { +func (s *Server) SetMirrorSettings(enabled bool, endpoints, skipEndpoints []string, preferredSource string) { s.mu.Lock() defer s.mu.Unlock() s.mirrorEnabled = enabled s.mirrorEndpoints = endpoints + s.skipMirrorEndpoints = skipEndpoints s.preferredSource = preferredSource } diff --git a/pkg/service/handlers/snapshot_integrity_test.go b/pkg/service/handlers/snapshot_integrity_test.go index 0c21ed5..996f9d3 100644 --- a/pkg/service/handlers/snapshot_integrity_test.go +++ b/pkg/service/handlers/snapshot_integrity_test.go @@ -27,7 +27,7 @@ func TestSnapshotIntegrity_SelfAndMirror(t *testing.T) { recorder := proxy.NewRecorder(tempDir) s := NewServer(ds, nil, "http://localhost:8000", false, false, true) s.SetRecorder(recorder) - s.SetMirrorSettings(true, []string{"/mirror/*"}, "local") + s.SetMirrorSettings(true, []string{"/mirror/*"}, nil, "local") // Upstream mock upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -39,7 +39,7 @@ func TestSnapshotIntegrity_SelfAndMirror(t *testing.T) { defer upstream.Close() // Configure mirror to point to our mock upstream - s.SetMirrorSettings(true, []string{"/mirror/*"}, "local") + s.SetMirrorSettings(true, []string{"/mirror/*"}, nil, "local") // We need to override the host in performMirror but for tests we can just mock it via env if needed or rely on the fact that performMirror uses r.Host handler := s.SnapshotMiddleware(s.MirrorMiddleware(s.RecordMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/service/handlers/web/index.html b/pkg/service/handlers/web/index.html index 781ab17..cbaf711 100644 --- a/pkg/service/handlers/web/index.html +++ b/pkg/service/handlers/web/index.html @@ -228,6 +228,13 @@ style="width: 100%; max-width: 600px; margin-top: 5px; font-family: monospace;" placeholder="/streaming/account/*/device/*/recent /accounts/*/devices/*/presets/*" > + +
s.trim()) .filter((s) => s !== ""), + skip_mirror_endpoints: document + .getElementById("skip-mirror-endpoints") + .value.split("\n") + .map((s) => s.trim()) + .filter((s) => s !== ""), internal_paths: document .getElementById("internal-paths") .value.split("\n") diff --git a/pkg/service/setup/setup.go b/pkg/service/setup/setup.go index 2072ab5..2745e51 100644 --- a/pkg/service/setup/setup.go +++ b/pkg/service/setup/setup.go @@ -73,6 +73,8 @@ type MigrationSummary struct { IsMigrated bool `json:"is_migrated"` MirrorEnabled bool `json:"mirror_enabled"` MirrorEndpoints []string `json:"mirror_endpoints,omitempty"` + SkipMirrorEndpoints []string `json:"skip_mirror_endpoints,omitempty"` + PreferredSource string `json:"preferred_source,omitempty"` } // SSHClient defines the interface for SSH operations. @@ -323,6 +325,8 @@ func (m *Manager) GetMigrationSummary(deviceIP, targetURL, proxyURL string, opti if err == nil { summary.MirrorEnabled = settings.MirrorEnabled summary.MirrorEndpoints = settings.MirrorEndpoints + summary.SkipMirrorEndpoints = settings.SkipMirrorEndpoints + summary.PreferredSource = settings.PreferredSource } }