mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-24 14:47:23 +00:00
Implement skip mirror endpoints to reduce false positives in parity checks (#126)
Added 'Skip Mirror Endpoints' setting to allow specific requests like `/oauth/device/*/music/musicprovider/15/token/cs3` to be handled exclusively locally, even when mirroring is enabled. Updated MirrorMiddleware to check against the skip list before performing mirroring or parity logic. Exposed the setting via the Web UI Settings tab and the CLI. Updated relevant tests to accommodate the configuration changes. Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
co-authored by
Junie
parent
d5d6585517
commit
9f7cb81b45
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"}]}}`
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -228,6 +228,13 @@
|
||||
style="width: 100%; max-width: 600px; margin-top: 5px; font-family: monospace;"
|
||||
placeholder="/streaming/account/*/device/*/recent /accounts/*/devices/*/presets/*"
|
||||
></textarea>
|
||||
<label for="skip-mirror-endpoints" style="display: block; margin-top: 10px;">Skip Mirror Endpoints (local only, one per line):</label>
|
||||
<textarea
|
||||
id="skip-mirror-endpoints"
|
||||
rows="2"
|
||||
style="width: 100%; max-width: 600px; margin-top: 5px; font-family: monospace;"
|
||||
placeholder="/oauth/device/*/music/musicprovider/*/token/cs3 /oauth/device/*/music/musicprovider/*/token"
|
||||
></textarea>
|
||||
<div
|
||||
class="info-box"
|
||||
style="
|
||||
|
||||
@@ -158,6 +158,9 @@ async function fetchSettings() {
|
||||
if (settings.mirror_endpoints) {
|
||||
document.getElementById("mirror-endpoints").value = settings.mirror_endpoints.join("\n");
|
||||
}
|
||||
if (settings.skip_mirror_endpoints) {
|
||||
document.getElementById("skip-mirror-endpoints").value = settings.skip_mirror_endpoints.join("\n");
|
||||
}
|
||||
if (settings.internal_paths) {
|
||||
document.getElementById("internal-paths").value = settings.internal_paths.join("\n");
|
||||
}
|
||||
@@ -220,6 +223,11 @@ async function updateSettings() {
|
||||
.value.split("\n")
|
||||
.map((s) => 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")
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user