mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 08:36:13 +00:00
Remove Soundcork fallback
This commit is contained in:
@@ -82,17 +82,6 @@ func main() {
|
||||
Usage: "Network interface to bind to",
|
||||
EnvVars: []string{"BIND_ADDR"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "soundcork-url",
|
||||
Usage: "URL for Soundcork-based service components (legacy)",
|
||||
Value: "http://localhost:8001",
|
||||
EnvVars: []string{"SOUNDCORK_BACKEND_URL", "TARGET_URL"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "enable-soundcork-proxy",
|
||||
Usage: "Enable proxying unknown requests to the Soundcork backend",
|
||||
EnvVars: []string{"ENABLE_SOUNDCORK_PROXY"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "data-dir",
|
||||
Usage: "Directory for persistent data",
|
||||
@@ -246,9 +235,8 @@ func main() {
|
||||
sm := setup.NewManager(config.serverURL, ds, cm)
|
||||
sm.MgmtUsername = config.mgmtUsername
|
||||
sm.MgmtPassword = config.mgmtPassword
|
||||
server := handlers.NewServer(ds, sm, config.serverURL, config.redact, config.logBody, config.record, config.enableSoundcorkProxy, config.migrationEnabled, config.migrationDryRun)
|
||||
server := handlers.NewServer(ds, sm, config.serverURL, config.redact, config.logBody, config.record, config.migrationEnabled, config.migrationDryRun)
|
||||
sm.GetDNSRunning = server.GetDNSRunning
|
||||
server.SetSoundcorkURL(config.soundcorkURL)
|
||||
server.SetHTTPServerURL(config.httpsServerURL)
|
||||
server.SetVersionInfo(version, commit, date)
|
||||
server.SetDiscoverySettings(config.discoveryInterval, persisted.DiscoveryEnabled)
|
||||
@@ -337,7 +325,7 @@ func main() {
|
||||
|
||||
r := setupRouter(server)
|
||||
|
||||
log.Printf("Go service starting on %s, proxying to %s", config.serverURL, config.soundcorkURL)
|
||||
log.Printf("Go service starting on %s", config.serverURL)
|
||||
|
||||
if tlsConfig != nil {
|
||||
startHTTPSServer(config.httpsAddr, r, tlsConfig, config.httpsServerURL)
|
||||
@@ -371,34 +359,32 @@ func showVersionInfo(_ *cli.Context) error {
|
||||
}
|
||||
|
||||
type serviceConfig struct {
|
||||
port string
|
||||
bindAddr string
|
||||
addr string
|
||||
soundcorkURL string
|
||||
dataDir string
|
||||
serverURL string
|
||||
httpsServerURL string
|
||||
httpsAddr string
|
||||
redact bool
|
||||
logBody bool
|
||||
record bool
|
||||
enableSoundcorkProxy bool
|
||||
dnsEnabled bool
|
||||
dnsUpstream string
|
||||
dnsBind string
|
||||
mirrorEnabled bool
|
||||
mirrorEndpoints []string
|
||||
internalPaths []string
|
||||
discoveryInterval time.Duration
|
||||
domains []string
|
||||
spotifyClientID string
|
||||
spotifyClientSecret string
|
||||
spotifyRedirectURI string
|
||||
mgmtUsername string
|
||||
mgmtPassword string
|
||||
migrationEnabled bool
|
||||
migrationDryRun bool
|
||||
preferredSource string
|
||||
port string
|
||||
bindAddr string
|
||||
addr string
|
||||
dataDir string
|
||||
serverURL string
|
||||
httpsServerURL string
|
||||
httpsAddr string
|
||||
redact bool
|
||||
logBody bool
|
||||
record bool
|
||||
dnsEnabled bool
|
||||
dnsUpstream string
|
||||
dnsBind string
|
||||
mirrorEnabled bool
|
||||
mirrorEndpoints []string
|
||||
internalPaths []string
|
||||
discoveryInterval time.Duration
|
||||
domains []string
|
||||
spotifyClientID string
|
||||
spotifyClientSecret string
|
||||
spotifyRedirectURI string
|
||||
mgmtUsername string
|
||||
mgmtPassword string
|
||||
migrationEnabled bool
|
||||
migrationDryRun bool
|
||||
preferredSource string
|
||||
}
|
||||
|
||||
func loadConfig(c *cli.Context) serviceConfig {
|
||||
@@ -410,7 +396,6 @@ func loadConfig(c *cli.Context) serviceConfig {
|
||||
addr = ":" + port
|
||||
}
|
||||
|
||||
soundcorkURL := c.String("soundcork-url")
|
||||
dataDir := c.String("data-dir")
|
||||
|
||||
hostname, _ := os.Hostname()
|
||||
@@ -442,7 +427,6 @@ func loadConfig(c *cli.Context) serviceConfig {
|
||||
redact := c.Bool("redact-logs")
|
||||
logBody := c.Bool("log-bodies")
|
||||
record := c.Bool("record-interactions")
|
||||
enableSoundcorkProxy := c.Bool("enable-soundcork-proxy")
|
||||
|
||||
dnsEnabled := c.Bool("dns-discovery")
|
||||
dnsUpstream := c.String("dns-upstream")
|
||||
@@ -470,34 +454,32 @@ func loadConfig(c *cli.Context) serviceConfig {
|
||||
preferredSource := c.String("preferred-source")
|
||||
|
||||
return serviceConfig{
|
||||
port: port,
|
||||
bindAddr: bindAddr,
|
||||
addr: addr,
|
||||
soundcorkURL: soundcorkURL,
|
||||
dataDir: dataDir,
|
||||
serverURL: serverURL,
|
||||
httpsServerURL: httpsServerURL,
|
||||
httpsAddr: httpsAddr,
|
||||
redact: redact,
|
||||
logBody: logBody,
|
||||
record: record,
|
||||
enableSoundcorkProxy: enableSoundcorkProxy,
|
||||
dnsEnabled: dnsEnabled,
|
||||
dnsUpstream: dnsUpstream,
|
||||
dnsBind: dnsBind,
|
||||
mirrorEnabled: mirrorEnabled,
|
||||
mirrorEndpoints: mirrorEndpoints,
|
||||
internalPaths: internalPaths,
|
||||
discoveryInterval: discoveryInterval,
|
||||
domains: domains,
|
||||
spotifyClientID: spotifyClientID,
|
||||
spotifyClientSecret: spotifyClientSecret,
|
||||
spotifyRedirectURI: spotifyRedirectURI,
|
||||
mgmtUsername: mgmtUsername,
|
||||
mgmtPassword: mgmtPassword,
|
||||
migrationEnabled: migrationEnabled,
|
||||
migrationDryRun: migrationDryRun,
|
||||
preferredSource: preferredSource,
|
||||
port: port,
|
||||
bindAddr: bindAddr,
|
||||
addr: addr,
|
||||
dataDir: dataDir,
|
||||
serverURL: serverURL,
|
||||
httpsServerURL: httpsServerURL,
|
||||
httpsAddr: httpsAddr,
|
||||
redact: redact,
|
||||
logBody: logBody,
|
||||
record: record,
|
||||
dnsEnabled: dnsEnabled,
|
||||
dnsUpstream: dnsUpstream,
|
||||
dnsBind: dnsBind,
|
||||
mirrorEnabled: mirrorEnabled,
|
||||
mirrorEndpoints: mirrorEndpoints,
|
||||
internalPaths: internalPaths,
|
||||
discoveryInterval: discoveryInterval,
|
||||
domains: domains,
|
||||
spotifyClientID: spotifyClientID,
|
||||
spotifyClientSecret: spotifyClientSecret,
|
||||
spotifyRedirectURI: spotifyRedirectURI,
|
||||
mgmtUsername: mgmtUsername,
|
||||
mgmtPassword: mgmtPassword,
|
||||
migrationEnabled: migrationEnabled,
|
||||
migrationDryRun: migrationDryRun,
|
||||
preferredSource: preferredSource,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -558,10 +540,6 @@ func applyPersistedSettings(ds *datastore.DataStore, config *serviceConfig) data
|
||||
config.serverURL = persisted.ServerURL
|
||||
}
|
||||
|
||||
if persisted.SoundcorkURL != "" {
|
||||
config.soundcorkURL = persisted.SoundcorkURL
|
||||
}
|
||||
|
||||
if persisted.HTTPServerURL != "" {
|
||||
config.httpsServerURL = persisted.HTTPServerURL
|
||||
}
|
||||
@@ -575,7 +553,6 @@ func applyPersistedSettings(ds *datastore.DataStore, config *serviceConfig) data
|
||||
config.redact = persisted.RedactLogs
|
||||
config.logBody = persisted.LogBodies
|
||||
config.record = persisted.RecordInteractions
|
||||
config.enableSoundcorkProxy = persisted.EnableSoundcorkProxy
|
||||
|
||||
config.dnsEnabled = persisted.DNSEnabled
|
||||
if len(persisted.DNSUpstream) > 0 {
|
||||
@@ -596,22 +573,20 @@ func applyPersistedSettings(ds *datastore.DataStore, config *serviceConfig) data
|
||||
|
||||
func createDefaultSettings(ds *datastore.DataStore, config serviceConfig) datastore.Settings {
|
||||
settings := datastore.Settings{
|
||||
ServerURL: config.serverURL,
|
||||
SoundcorkURL: config.soundcorkURL,
|
||||
HTTPServerURL: config.httpsServerURL,
|
||||
RedactLogs: config.redact,
|
||||
LogBodies: config.logBody,
|
||||
RecordInteractions: config.record,
|
||||
DiscoveryInterval: config.discoveryInterval.String(),
|
||||
DiscoveryEnabled: true,
|
||||
EnableSoundcorkProxy: config.enableSoundcorkProxy,
|
||||
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,
|
||||
PreferredSource: config.preferredSource,
|
||||
InternalPaths: config.internalPaths,
|
||||
Shortcuts: map[string]int{
|
||||
"/.well-known/appspecific/com.chrome.devtools.json": http.StatusNotFound,
|
||||
"/sw.js": http.StatusNotFound,
|
||||
|
||||
@@ -18,10 +18,9 @@ func TestApplyPersistedSettings(t *testing.T) {
|
||||
|
||||
t.Run("overrides true with false", func(t *testing.T) {
|
||||
config := &serviceConfig{
|
||||
redact: true,
|
||||
logBody: true,
|
||||
record: true,
|
||||
enableSoundcorkProxy: true,
|
||||
redact: true,
|
||||
logBody: true,
|
||||
record: true,
|
||||
}
|
||||
|
||||
// Simulate the bug by using the old bitwise OR logic in the test,
|
||||
@@ -29,10 +28,9 @@ func TestApplyPersistedSettings(t *testing.T) {
|
||||
// config.redact = config.redact || false -> stays true
|
||||
|
||||
settings := datastore.Settings{
|
||||
RedactLogs: false,
|
||||
LogBodies: false,
|
||||
RecordInteractions: false,
|
||||
EnableSoundcorkProxy: false,
|
||||
RedactLogs: false,
|
||||
LogBodies: false,
|
||||
RecordInteractions: false,
|
||||
}
|
||||
err := ds.SaveSettings(settings)
|
||||
if err != nil {
|
||||
@@ -50,9 +48,6 @@ func TestApplyPersistedSettings(t *testing.T) {
|
||||
if config.record != false {
|
||||
t.Errorf("Expected record to be false, got true")
|
||||
}
|
||||
if config.enableSoundcorkProxy != false {
|
||||
t.Errorf("Expected enableSoundcorkProxy to be false, got true")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("retains false when settings are false", func(t *testing.T) {
|
||||
|
||||
@@ -875,23 +875,21 @@ func (ds *DataStore) GetETagForAccount(account, device string) int64 {
|
||||
|
||||
// Settings represents the global service settings.
|
||||
type Settings struct {
|
||||
ServerURL string `json:"server_url"`
|
||||
SoundcorkURL string `json:"soundcork_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"`
|
||||
EnableSoundcorkProxy bool `json:"enable_soundcork_proxy"`
|
||||
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"`
|
||||
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.
|
||||
|
||||
@@ -382,7 +382,6 @@ func TestSettingsPersistence(t *testing.T) {
|
||||
|
||||
settings := Settings{
|
||||
ServerURL: "http://myserver:8000",
|
||||
SoundcorkURL: "http://myproxy:8001",
|
||||
LogBodies: true,
|
||||
DiscoveryInterval: "10m",
|
||||
DiscoveryEnabled: true,
|
||||
|
||||
@@ -154,7 +154,7 @@ func TestComprehensiveMigration_MultipleExistingDevices(t *testing.T) {
|
||||
|
||||
deviceIP := server.URL[len("http://"):]
|
||||
sm := setup.NewManager(server.URL, ds, nil)
|
||||
srv := NewServer(ds, sm, server.URL, false, false, false, false, false, false)
|
||||
srv := NewServer(ds, sm, server.URL, false, false, false, false, false)
|
||||
|
||||
// Simulate device rediscovery
|
||||
discoveredDevice := models.DiscoveredDevice{
|
||||
@@ -233,7 +233,7 @@ func TestFindAllExistingDeviceVariants_MatchingCriteria(t *testing.T) {
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
ds := datastore.NewDataStore(tempDir)
|
||||
srv := NewServer(ds, nil, "http://localhost", false, false, false, false, false, false)
|
||||
srv := NewServer(ds, nil, "http://localhost", false, false, false, false, false)
|
||||
accountID := "testaccount"
|
||||
|
||||
// Create devices that should match various criteria
|
||||
@@ -344,7 +344,7 @@ func TestMigration_EdgeCases(t *testing.T) {
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
ds := datastore.NewDataStore(tempDir)
|
||||
srv := NewServer(ds, nil, "http://localhost", false, false, false, false, false, false)
|
||||
srv := NewServer(ds, nil, "http://localhost", false, false, false, false, false)
|
||||
accountID := "testaccount"
|
||||
|
||||
t.Run("NoExistingDevices", func(t *testing.T) {
|
||||
|
||||
@@ -16,7 +16,7 @@ func TestDeviceMigration_DirectoryRename(t *testing.T) {
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
ds := datastore.NewDataStore(tempDir)
|
||||
srv := NewServer(ds, nil, "http://localhost", false, false, false, false, true, false)
|
||||
srv := NewServer(ds, nil, "http://localhost", false, false, false, true, false)
|
||||
|
||||
accountID := "test-account"
|
||||
macAddress := "A81B6A536A98"
|
||||
@@ -121,7 +121,7 @@ func TestDeviceMigration_NoExistingTarget(t *testing.T) {
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
ds := datastore.NewDataStore(tempDir)
|
||||
srv := NewServer(ds, nil, "http://localhost", false, false, false, false, true, false)
|
||||
srv := NewServer(ds, nil, "http://localhost", false, false, false, true, false)
|
||||
|
||||
accountID := "test-account"
|
||||
macAddress := "A81B6A536A98"
|
||||
@@ -189,7 +189,7 @@ func TestDeviceMigration_ExistingTargetRemoved(t *testing.T) {
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
ds := datastore.NewDataStore(tempDir)
|
||||
srv := NewServer(ds, nil, "http://localhost", false, false, false, false, true, false)
|
||||
srv := NewServer(ds, nil, "http://localhost", false, false, false, true, false)
|
||||
|
||||
accountID := "test-account"
|
||||
macAddress := "A81B6A536A98"
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
func TestEventLog(t *testing.T) {
|
||||
ds := datastore.NewDataStore(t.TempDir())
|
||||
s := NewServer(ds, nil, "http://localhost", false, false, false, false, false, false)
|
||||
s := NewServer(ds, nil, "http://localhost", false, false, false, false, false)
|
||||
|
||||
r := chi.NewRouter()
|
||||
r.Post("/streaming/stats/usage", s.HandleUsageStats)
|
||||
|
||||
@@ -20,7 +20,7 @@ type healthResp struct {
|
||||
|
||||
func TestHealthEndpoint(t *testing.T) {
|
||||
r := chi.NewRouter()
|
||||
srv := NewServer(nil, nil, "http://localhost", false, false, false, false, false, false)
|
||||
srv := NewServer(nil, nil, "http://localhost", false, false, false, false, false)
|
||||
r.Get("/health", srv.HandleHealth)
|
||||
|
||||
ts := httptest.NewServer(r)
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
func TestHandleMgmtSpotifyInit(t *testing.T) {
|
||||
s := NewServer(nil, nil, "http://localhost", false, false, false, false, false, false)
|
||||
s := NewServer(nil, nil, "http://localhost", false, false, false, false, false)
|
||||
// No spotify service configured
|
||||
req := httptest.NewRequest("POST", "/mgmt/spotify/init", nil)
|
||||
w := httptest.NewRecorder()
|
||||
@@ -45,7 +45,7 @@ func TestHandleMgmtSpotifyInit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandleMgmtSpotifyAccounts(t *testing.T) {
|
||||
s := NewServer(nil, nil, "http://localhost", false, false, false, false, false, false)
|
||||
s := NewServer(nil, nil, "http://localhost", false, false, false, false, false)
|
||||
svc := spotify.NewSpotifyService("cid", "secret", "http://localhost/cb", t.TempDir())
|
||||
s.SetSpotifyService(svc)
|
||||
|
||||
@@ -91,7 +91,7 @@ func TestHandleMgmtListSpeakers(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandleMgmtSpotifyCallback(t *testing.T) {
|
||||
s := NewServer(nil, nil, "http://localhost", false, false, false, false, false, false)
|
||||
s := NewServer(nil, nil, "http://localhost", false, false, false, false, false)
|
||||
svc := spotify.NewSpotifyService("cid", "secret", "http://localhost/cb", t.TempDir())
|
||||
s.SetSpotifyService(svc)
|
||||
|
||||
@@ -150,7 +150,7 @@ func TestHandleMgmtSpotifyCallback(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandleMgmtSpotifyConfirm(t *testing.T) {
|
||||
s := NewServer(nil, nil, "http://localhost", false, false, false, false, false, false)
|
||||
s := NewServer(nil, nil, "http://localhost", false, false, false, false, false)
|
||||
svc := spotify.NewSpotifyService("cid", "secret", "http://localhost/cb", t.TempDir())
|
||||
s.SetSpotifyService(svc)
|
||||
|
||||
@@ -191,7 +191,7 @@ func TestHandleMgmtDeviceEvents(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBasicAuthMgmt(t *testing.T) {
|
||||
s := NewServer(nil, nil, "http://localhost", false, false, false, false, false, false)
|
||||
s := NewServer(nil, nil, "http://localhost", false, false, false, false, false)
|
||||
s.SetMgmtConfig("admin", "secret123")
|
||||
|
||||
handler := s.BasicAuthMgmt()(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -98,84 +98,9 @@ func (s *Server) ServeProxy(target *url.URL) http.HandlerFunc {
|
||||
|
||||
// HandleNotFound handles requests that don't match any route.
|
||||
func (s *Server) HandleNotFound(w http.ResponseWriter, r *http.Request) {
|
||||
if s.enableSoundcorkProxy {
|
||||
s.HandleSoundcorkWithFallback(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
s.HandleBoseProxy(w, r)
|
||||
}
|
||||
|
||||
// HandleSoundcorkWithFallback tries Soundcork first, then Bose if Soundcork returns 404 or fails.
|
||||
func (s *Server) HandleSoundcorkWithFallback(w http.ResponseWriter, r *http.Request) {
|
||||
target, _ := url.Parse(s.soundcorkURL)
|
||||
|
||||
// Buffer request body if any, to allow multiple proxy attempts
|
||||
var bodyBytes []byte
|
||||
if r.Body != nil {
|
||||
bodyBytes, _ = io.ReadAll(r.Body)
|
||||
_ = r.Body.Close()
|
||||
}
|
||||
|
||||
// We use a custom response writer to catch 404s
|
||||
rw := &fallbackResponseWriter{
|
||||
ResponseWriter: w,
|
||||
statusCode: http.StatusOK,
|
||||
buffer: &bytes.Buffer{},
|
||||
}
|
||||
|
||||
// Create a shallow copy of the request to avoid side effects between attempts
|
||||
r2 := r.Clone(r.Context())
|
||||
if bodyBytes != nil {
|
||||
r2.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||
} else {
|
||||
r2.Body = nil
|
||||
}
|
||||
|
||||
// Remove RequestURI as it's not allowed in client requests
|
||||
r2.RequestURI = ""
|
||||
|
||||
s.ServeProxy(target)(rw, r2)
|
||||
|
||||
if rw.statusCode == http.StatusNotFound || rw.statusCode == http.StatusBadGateway || rw.statusCode == http.StatusServiceUnavailable {
|
||||
log.Printf("[PROXY] Soundcork returned %d for %s, falling back to Bose", rw.statusCode, r.URL.Path)
|
||||
|
||||
if !rw.wroteHeader {
|
||||
// Restore original body if any
|
||||
if bodyBytes != nil {
|
||||
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||
}
|
||||
|
||||
s.HandleBoseProxy(w, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type fallbackResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
statusCode int
|
||||
wroteHeader bool
|
||||
buffer *bytes.Buffer
|
||||
}
|
||||
|
||||
func (rw *fallbackResponseWriter) WriteHeader(code int) {
|
||||
rw.statusCode = code
|
||||
if code != http.StatusNotFound && code != http.StatusBadGateway && code != http.StatusServiceUnavailable {
|
||||
rw.wroteHeader = true
|
||||
rw.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
}
|
||||
|
||||
func (rw *fallbackResponseWriter) Write(b []byte) (int, error) {
|
||||
if rw.statusCode == http.StatusNotFound || rw.statusCode == http.StatusBadGateway || rw.statusCode == http.StatusServiceUnavailable {
|
||||
return len(b), nil // Drop the body
|
||||
}
|
||||
|
||||
rw.wroteHeader = true
|
||||
|
||||
return rw.ResponseWriter.Write(b)
|
||||
}
|
||||
|
||||
// HandleBoseProxy proxies the request to the Bose upstream.
|
||||
func (s *Server) HandleBoseProxy(w http.ResponseWriter, r *http.Request) {
|
||||
host := r.Host
|
||||
|
||||
@@ -33,7 +33,7 @@ func TestHandleProxyRequest_RequestBodyRecording(t *testing.T) {
|
||||
defer backend.Close()
|
||||
|
||||
ds := datastore.NewDataStore(filepath.Join(tmpDir, "test.db"))
|
||||
server := NewServer(ds, nil, "http://localhost", false, false, false, false, false, false)
|
||||
server := NewServer(ds, nil, "http://localhost", false, false, false, false, false)
|
||||
server.recordEnabled = true
|
||||
server.proxyLogBody = true
|
||||
recorder := proxy.NewRecorder(tmpDir)
|
||||
|
||||
@@ -148,7 +148,7 @@ func (s *Server) HandleGetSettings(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
s.mu.RLock()
|
||||
serverURL, soundcorkURL, httpsServerURL := s.serverURL, s.soundcorkURL, s.httpsServerURL
|
||||
serverURL, httpsServerURL := s.serverURL, s.httpsServerURL
|
||||
discoveryInterval := s.discoveryInterval.String()
|
||||
discoveryEnabled := s.discoveryEnabled
|
||||
dnsEnabled := s.dnsEnabled
|
||||
@@ -158,7 +158,6 @@ func (s *Server) HandleGetSettings(w http.ResponseWriter, _ *http.Request) {
|
||||
mirrorEndpoints := s.mirrorEndpoints
|
||||
preferredSource := s.preferredSource
|
||||
internalPaths := s.internalPaths
|
||||
enableSoundcorkProxy := s.enableSoundcorkProxy
|
||||
redact, logBody, record := s.proxyRedact, s.proxyLogBody, s.recordEnabled
|
||||
shortcuts := s.shortcuts
|
||||
spotifyConfigured := s.spotifyService != nil
|
||||
@@ -167,26 +166,24 @@ 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,
|
||||
"soundcork_url": soundcorkURL,
|
||||
"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,
|
||||
"enable_soundcork_proxy": enableSoundcorkProxy,
|
||||
"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,
|
||||
"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
|
||||
@@ -196,19 +193,17 @@ 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"`
|
||||
SoundcorkURL string `json:"soundcork_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"`
|
||||
EnableSoundcorkProxy bool `json:"enable_soundcork_proxy"`
|
||||
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"`
|
||||
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)
|
||||
@@ -230,7 +225,6 @@ func (s *Server) HandleUpdateSettings(w http.ResponseWriter, r *http.Request) {
|
||||
s.mu.Lock()
|
||||
s.serverURL = settings.ServerURL
|
||||
|
||||
s.soundcorkURL = settings.SoundcorkURL
|
||||
if settings.DiscoveryInterval != "" {
|
||||
s.discoveryInterval = interval
|
||||
}
|
||||
@@ -258,7 +252,6 @@ func (s *Server) HandleUpdateSettings(w http.ResponseWriter, r *http.Request) {
|
||||
s.preferredSource = settings.PreferredSource
|
||||
s.internalPaths = settings.InternalPaths
|
||||
|
||||
s.enableSoundcorkProxy = settings.EnableSoundcorkProxy
|
||||
if settings.Shortcuts != nil {
|
||||
s.shortcuts = settings.Shortcuts
|
||||
}
|
||||
@@ -276,23 +269,21 @@ 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,
|
||||
SoundcorkURL: s.soundcorkURL,
|
||||
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,
|
||||
EnableSoundcorkProxy: s.enableSoundcorkProxy,
|
||||
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,
|
||||
PreferredSource: s.preferredSource,
|
||||
InternalPaths: s.internalPaths,
|
||||
Shortcuts: s.shortcuts,
|
||||
})
|
||||
|
||||
dnsEnabled := s.dnsEnabled
|
||||
@@ -793,13 +784,12 @@ func (s *Server) HandleBackupConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *Server) HandleGetProxySettings(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
redact, logBody, record, enableSoundcorkProxy := s.GetProxySettings()
|
||||
redact, logBody, record := s.GetProxySettings()
|
||||
|
||||
if err := json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"redact": redact,
|
||||
"log_body": logBody,
|
||||
"record": record,
|
||||
"enable_soundcork_proxy": enableSoundcorkProxy,
|
||||
"redact": redact,
|
||||
"log_body": logBody,
|
||||
"record": record,
|
||||
}); err != nil {
|
||||
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
|
||||
return
|
||||
@@ -824,10 +814,9 @@ func (s *Server) HandleGetCACert(w http.ResponseWriter, _ *http.Request) {
|
||||
// HandleUpdateProxySettings updates the proxy settings.
|
||||
func (s *Server) HandleUpdateProxySettings(w http.ResponseWriter, r *http.Request) {
|
||||
var settings struct {
|
||||
Redact bool `json:"redact"`
|
||||
LogBody bool `json:"log_body"`
|
||||
Record bool `json:"record"`
|
||||
EnableSoundcorkProxy bool `json:"enable_soundcork_proxy"`
|
||||
Redact bool `json:"redact"`
|
||||
LogBody bool `json:"log_body"`
|
||||
Record bool `json:"record"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&settings); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
@@ -838,7 +827,6 @@ func (s *Server) HandleUpdateProxySettings(w http.ResponseWriter, r *http.Reques
|
||||
s.proxyRedact = settings.Redact
|
||||
s.proxyLogBody = settings.LogBody
|
||||
s.recordEnabled = settings.Record
|
||||
s.enableSoundcorkProxy = settings.EnableSoundcorkProxy
|
||||
|
||||
if s.recorder != nil {
|
||||
s.recorder.Redact = settings.Redact
|
||||
@@ -846,22 +834,20 @@ func (s *Server) HandleUpdateProxySettings(w http.ResponseWriter, r *http.Reques
|
||||
|
||||
// Persist to datastore
|
||||
// Access fields directly since we already hold the lock
|
||||
serverURL, soundcorkURL, httpsServerURL := s.serverURL, s.soundcorkURL, s.httpsServerURL
|
||||
serverURL, httpsServerURL := s.serverURL, s.httpsServerURL
|
||||
discoveryInterval := s.discoveryInterval.String()
|
||||
discoveryEnabled := s.discoveryEnabled
|
||||
|
||||
log.Printf("Saving updated proxy settings to %s/settings.json", s.ds.DataDir)
|
||||
err := s.ds.SaveSettings(datastore.Settings{
|
||||
ServerURL: serverURL,
|
||||
SoundcorkURL: soundcorkURL,
|
||||
HTTPServerURL: httpsServerURL,
|
||||
RedactLogs: s.proxyRedact,
|
||||
LogBodies: s.proxyLogBody,
|
||||
RecordInteractions: s.recordEnabled,
|
||||
DiscoveryInterval: discoveryInterval,
|
||||
DiscoveryEnabled: discoveryEnabled,
|
||||
EnableSoundcorkProxy: s.enableSoundcorkProxy,
|
||||
Shortcuts: s.shortcuts,
|
||||
ServerURL: serverURL,
|
||||
HTTPServerURL: httpsServerURL,
|
||||
RedactLogs: s.proxyRedact,
|
||||
LogBodies: s.proxyLogBody,
|
||||
RecordInteractions: s.recordEnabled,
|
||||
DiscoveryInterval: discoveryInterval,
|
||||
DiscoveryEnabled: discoveryEnabled,
|
||||
Shortcuts: s.shortcuts,
|
||||
})
|
||||
s.mu.Unlock()
|
||||
|
||||
|
||||
@@ -101,8 +101,7 @@ func TestProxySettingsAPI(t *testing.T) {
|
||||
|
||||
// 3. Test System Settings POST
|
||||
sysUpdate := map[string]string{
|
||||
"server_url": "http://new-server:8000",
|
||||
"soundcork_url": "http://new-proxy:8001",
|
||||
"server_url": "http://new-server:8000",
|
||||
}
|
||||
|
||||
sysBody, err := json.Marshal(sysUpdate)
|
||||
@@ -122,15 +121,14 @@ func TestProxySettingsAPI(t *testing.T) {
|
||||
}
|
||||
|
||||
// Verify server state
|
||||
sURL, pURL, _ := server.GetSettings()
|
||||
if sURL != "http://new-server:8000" || pURL != "http://new-proxy:8001" {
|
||||
t.Errorf("POST /setup/settings: Server state did not update: serverURL=%s, soundcorkURL=%s", sURL, pURL)
|
||||
sURL, _ := server.GetSettings()
|
||||
if sURL != "http://new-server:8000" {
|
||||
t.Errorf("POST /setup/settings: Server state did not update: serverURL=%s", sURL)
|
||||
}
|
||||
|
||||
// 4. Test Mirror Settings persistence
|
||||
mirrorUpdate := map[string]interface{}{
|
||||
"server_url": "http://mirror-test:8000",
|
||||
"soundcork_url": "http://mirror-test:8001",
|
||||
"mirror_enabled": true,
|
||||
"mirror_endpoints": []string{"/test/*"},
|
||||
"internal_paths": []string{"/setup/*"},
|
||||
|
||||
@@ -20,7 +20,7 @@ func TestStatsHandlers(t *testing.T) {
|
||||
defer func() { _ = os.RemoveAll(tempDir) }()
|
||||
|
||||
ds := datastore.NewDataStore(tempDir)
|
||||
s := NewServer(ds, nil, "http://localhost", false, false, false, false, false, false)
|
||||
s := NewServer(ds, nil, "http://localhost", false, false, false, false, false)
|
||||
|
||||
t.Run("HandleUsageStats XML", func(t *testing.T) {
|
||||
xmlData := `
|
||||
|
||||
@@ -23,7 +23,7 @@ func TestInteractionHandlers(t *testing.T) {
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
ds := datastore.NewDataStore(filepath.Join(tmpDir, "test.db"))
|
||||
server := NewServer(ds, nil, "http://localhost", false, false, false, false, false, false)
|
||||
server := NewServer(ds, nil, "http://localhost", false, false, false, false, false)
|
||||
|
||||
t.Run("HandleGetInteractionStats_NoRecorder", func(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/setup/interaction-stats", nil)
|
||||
@@ -151,7 +151,7 @@ func TestRecordMiddleware(t *testing.T) {
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
ds := datastore.NewDataStore(filepath.Join(tmpDir, "test.db"))
|
||||
server := NewServer(ds, nil, "http://localhost", false, false, true, false, false, false)
|
||||
server := NewServer(ds, nil, "http://localhost", false, false, true, false, false)
|
||||
recorder := proxy.NewRecorder(tmpDir)
|
||||
server.SetRecorder(recorder)
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ func TestMACBasedDeviceDiscovery_Integration(t *testing.T) {
|
||||
sm := setup.NewManager(server.URL, ds, nil)
|
||||
|
||||
// Create server instance
|
||||
srv := NewServer(ds, sm, "http://localhost", false, false, false, false, false, false)
|
||||
srv := NewServer(ds, sm, "http://localhost", false, false, false, false, false)
|
||||
|
||||
t.Logf("Test scenario:")
|
||||
t.Logf(" Device IP: %s", deviceIP)
|
||||
@@ -311,7 +311,7 @@ func TestMACBasedDeviceDiscovery_MigrationScenario(t *testing.T) {
|
||||
|
||||
deviceIP := server.URL[len("http://"):]
|
||||
sm := setup.NewManager(server.URL, ds, nil)
|
||||
srv := NewServer(ds, sm, server.URL, false, false, false, false, false, false)
|
||||
srv := NewServer(ds, sm, server.URL, false, false, false, false, false)
|
||||
|
||||
// 3. Simulate rediscovery of the same device (now with /info working)
|
||||
discoveredDevice := models.DiscoveredDevice{
|
||||
@@ -380,7 +380,7 @@ func TestMACBasedDeviceDiscovery_FallbackScenario(t *testing.T) {
|
||||
ds := datastore.NewDataStore(tempDir)
|
||||
sm := setup.NewManager(server.URL, ds, nil)
|
||||
|
||||
srv := NewServer(ds, sm, server.URL, false, false, false, false, false, false)
|
||||
srv := NewServer(ds, sm, server.URL, false, false, false, false, false)
|
||||
|
||||
// Simulate device discovery with UPnP providing serial
|
||||
discoveredDevice := models.DiscoveredDevice{
|
||||
|
||||
@@ -97,7 +97,7 @@ func TestMacMappingIntegration_HTTPHandler(t *testing.T) {
|
||||
t.Fatalf("failed to initialize datastore: %v", err)
|
||||
}
|
||||
|
||||
server := NewServer(ds, nil, "http://localhost", false, false, false, false, false, false)
|
||||
server := NewServer(ds, nil, "http://localhost", false, false, false, false, false)
|
||||
|
||||
// Setup router with the exact same route as in production
|
||||
router := chi.NewRouter()
|
||||
|
||||
@@ -6,8 +6,7 @@ import (
|
||||
)
|
||||
|
||||
func setupRouter(targetURL string, ds *datastore.DataStore) (*chi.Mux, *Server) {
|
||||
server := NewServer(ds, nil, targetURL, false, false, false, false, false, false)
|
||||
server.SetSoundcorkURL(targetURL)
|
||||
server := NewServer(ds, nil, targetURL, false, false, false, false, false)
|
||||
|
||||
r := chi.NewRouter()
|
||||
r.Use(server.OriginMiddleware)
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestMirrorMiddleware_PreferredSource(t *testing.T) {
|
||||
defer upstreamServer.Close()
|
||||
|
||||
// 3. Setup our server with MirrorMiddleware
|
||||
server := NewServer(ds, nil, "http://localhost:8000", false, false, false, false, false, false)
|
||||
server := NewServer(ds, nil, "http://localhost:8000", false, false, false, false, false)
|
||||
server.SetMirrorSettings(true, []string{"/test/local"}, "local")
|
||||
|
||||
// We need to trick performMirror to use our mock upstream.
|
||||
@@ -119,7 +119,7 @@ func TestSettingsAPI_PreferredSource(t *testing.T) {
|
||||
ds := datastore.NewDataStore(tempDir)
|
||||
_ = ds.Initialize()
|
||||
|
||||
server := NewServer(ds, nil, "http://localhost:8000", false, false, false, false, false, false)
|
||||
server := NewServer(ds, nil, "http://localhost:8000", false, false, false, false, false)
|
||||
|
||||
// Test GET initial
|
||||
req := httptest.NewRequest("GET", "/setup/settings", nil)
|
||||
|
||||
@@ -25,40 +25,38 @@ import (
|
||||
|
||||
// Server handles HTTP requests for the SoundTouch service.
|
||||
type Server struct {
|
||||
ds *datastore.DataStore
|
||||
sm *setup.Manager
|
||||
migrationManager *migration.Manager
|
||||
mu sync.RWMutex
|
||||
serverURL string
|
||||
soundcorkURL string
|
||||
httpsServerURL string
|
||||
discovering bool
|
||||
proxyRedact bool
|
||||
proxyLogBody bool
|
||||
recordEnabled bool
|
||||
discoveryInterval time.Duration
|
||||
discoveryEnabled bool
|
||||
dnsEnabled bool
|
||||
dnsUpstream []string
|
||||
dnsBindAddr string
|
||||
mirrorEnabled bool
|
||||
mirrorEndpoints []string
|
||||
preferredSource string
|
||||
internalPaths []string
|
||||
enableSoundcorkProxy bool
|
||||
shortcuts map[string]int
|
||||
recorder *proxy.Recorder
|
||||
dnsDiscovery *discovery.DNSDiscovery
|
||||
UpstreamProxy http.Handler
|
||||
Version string
|
||||
Commit string
|
||||
Date string
|
||||
mgmtUsername string
|
||||
mgmtPassword string
|
||||
spotifyClientID string
|
||||
spotifyClientSecret string
|
||||
spotifyRedirectURI string
|
||||
spotifyService *spotify.Service
|
||||
ds *datastore.DataStore
|
||||
sm *setup.Manager
|
||||
migrationManager *migration.Manager
|
||||
mu sync.RWMutex
|
||||
serverURL string
|
||||
httpsServerURL string
|
||||
discovering bool
|
||||
proxyRedact bool
|
||||
proxyLogBody bool
|
||||
recordEnabled bool
|
||||
discoveryInterval time.Duration
|
||||
discoveryEnabled bool
|
||||
dnsEnabled bool
|
||||
dnsUpstream []string
|
||||
dnsBindAddr string
|
||||
mirrorEnabled bool
|
||||
mirrorEndpoints []string
|
||||
preferredSource string
|
||||
internalPaths []string
|
||||
shortcuts map[string]int
|
||||
recorder *proxy.Recorder
|
||||
dnsDiscovery *discovery.DNSDiscovery
|
||||
UpstreamProxy http.Handler
|
||||
Version string
|
||||
Commit string
|
||||
Date string
|
||||
mgmtUsername string
|
||||
mgmtPassword string
|
||||
spotifyClientID string
|
||||
spotifyClientSecret string
|
||||
spotifyRedirectURI string
|
||||
spotifyService *spotify.Service
|
||||
}
|
||||
|
||||
// RequestSnapshot represents an immutable snapshot of an HTTP request.
|
||||
@@ -83,7 +81,7 @@ var bufferPool = sync.Pool{
|
||||
}
|
||||
|
||||
// NewServer creates a new SoundTouch service server.
|
||||
func NewServer(ds *datastore.DataStore, sm *setup.Manager, serverURL string, proxyRedact, proxyLogBody, recordEnabled, enableSoundcorkProxy, migrationEnabled, migrationDryRun bool) *Server {
|
||||
func NewServer(ds *datastore.DataStore, sm *setup.Manager, serverURL string, proxyRedact, proxyLogBody, recordEnabled, migrationEnabled, migrationDryRun bool) *Server {
|
||||
// Initialize migration manager
|
||||
migrationConfig := migration.Config{
|
||||
Enabled: migrationEnabled,
|
||||
@@ -91,17 +89,15 @@ func NewServer(ds *datastore.DataStore, sm *setup.Manager, serverURL string, pro
|
||||
}
|
||||
|
||||
s := &Server{
|
||||
ds: ds,
|
||||
sm: sm,
|
||||
migrationManager: migration.NewManager(ds, migrationConfig),
|
||||
serverURL: serverURL,
|
||||
soundcorkURL: "http://localhost:8001",
|
||||
proxyRedact: proxyRedact,
|
||||
proxyLogBody: proxyLogBody,
|
||||
recordEnabled: recordEnabled,
|
||||
enableSoundcorkProxy: enableSoundcorkProxy,
|
||||
discoveryInterval: 5 * time.Minute,
|
||||
discoveryEnabled: true,
|
||||
ds: ds,
|
||||
sm: sm,
|
||||
migrationManager: migration.NewManager(ds, migrationConfig),
|
||||
serverURL: serverURL,
|
||||
proxyRedact: proxyRedact,
|
||||
proxyLogBody: proxyLogBody,
|
||||
recordEnabled: recordEnabled,
|
||||
discoveryInterval: 5 * time.Minute,
|
||||
discoveryEnabled: true,
|
||||
}
|
||||
|
||||
return s
|
||||
@@ -301,14 +297,6 @@ func (s *Server) SetHTTPServerURL(url string) {
|
||||
s.httpsServerURL = url
|
||||
}
|
||||
|
||||
// SetSoundcorkURL sets the URL for the Soundcork backend.
|
||||
func (s *Server) SetSoundcorkURL(url string) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.soundcorkURL = url
|
||||
}
|
||||
|
||||
// SetRecorder sets the recorder for the server.
|
||||
func (s *Server) SetRecorder(r *proxy.Recorder) {
|
||||
s.mu.Lock()
|
||||
@@ -374,11 +362,11 @@ func (s *Server) GetRecordEnabled() bool {
|
||||
}
|
||||
|
||||
// GetSettings returns the current server settings.
|
||||
func (s *Server) GetSettings() (string, string, string) {
|
||||
func (s *Server) GetSettings() (string, string) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.serverURL, s.soundcorkURL, s.httpsServerURL
|
||||
return s.serverURL, s.httpsServerURL
|
||||
}
|
||||
|
||||
// IsSpotifyConfigured returns whether Spotify integration is configured.
|
||||
@@ -390,11 +378,11 @@ func (s *Server) IsSpotifyConfigured() bool {
|
||||
}
|
||||
|
||||
// GetProxySettings returns the current proxy settings.
|
||||
func (s *Server) GetProxySettings() (bool, bool, bool, bool) {
|
||||
func (s *Server) GetProxySettings() (bool, bool, bool) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.proxyRedact, s.proxyLogBody, s.recordEnabled, s.enableSoundcorkProxy
|
||||
return s.proxyRedact, s.proxyLogBody, s.recordEnabled
|
||||
}
|
||||
|
||||
// DiscoverDevices starts a background device discovery process.
|
||||
|
||||
@@ -16,7 +16,7 @@ func TestMergeOverlappingDevices(t *testing.T) {
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
ds := datastore.NewDataStore(tempDir)
|
||||
s := NewServer(ds, nil, "http://localhost", false, false, false, false, false, false)
|
||||
s := NewServer(ds, nil, "http://localhost", false, false, false, false, false)
|
||||
|
||||
// Case 1: IP-only entry and Serial-based entry for the same IP
|
||||
ip := "192.168.1.100"
|
||||
@@ -74,7 +74,7 @@ func TestFindExistingDeviceID(t *testing.T) {
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
ds := datastore.NewDataStore(tempDir)
|
||||
s := NewServer(ds, nil, "http://localhost", false, false, false, false, false, false)
|
||||
s := NewServer(ds, nil, "http://localhost", false, false, false, false, false)
|
||||
|
||||
ip := "192.168.1.101"
|
||||
serial := "SERIAL456"
|
||||
|
||||
@@ -25,7 +25,7 @@ func TestSnapshotIntegrity_SelfAndMirror(t *testing.T) {
|
||||
|
||||
ds := datastore.NewDataStore(tempDir)
|
||||
recorder := proxy.NewRecorder(tempDir)
|
||||
s := NewServer(ds, nil, "http://localhost:8000", false, false, true, false, false, false)
|
||||
s := NewServer(ds, nil, "http://localhost:8000", false, false, true, false, false)
|
||||
s.SetRecorder(recorder)
|
||||
s.SetMirrorSettings(true, []string{"/mirror/*"}, "local")
|
||||
|
||||
|
||||
@@ -87,11 +87,6 @@
|
||||
<input type="text" id="target-domain" placeholder="http://192.168.x.x:8000" style="width: 300px;">
|
||||
<span style="font-size: 0.8em; color: #666;">(Standard services URL)</span>
|
||||
</div>
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label for="soundcork-url">Soundcork URL:</label>
|
||||
<input type="text" id="soundcork-url" placeholder="http://192.168.x.x:8001" style="width: 300px;">
|
||||
<span style="font-size: 0.8em; color: #666;">(Soundcork services URL)</span>
|
||||
</div>
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label for="discovery-interval">Discovery Interval:</label>
|
||||
<input type="text" id="discovery-interval" placeholder="5m" style="width: 100px;">
|
||||
@@ -151,7 +146,6 @@
|
||||
<div style="margin-top: 5px;">
|
||||
<label style="display: block; margin-bottom: 5px;"><input type="checkbox" id="proxy-redact" onchange="updateProxySettings()"> Redact Sensitive Headers</label>
|
||||
<label style="display: block; margin-bottom: 5px;"><input type="checkbox" id="proxy-log-body" onchange="updateProxySettings()"> Log Bodies</label>
|
||||
<label style="display: block; margin-bottom: 5px;"><input type="checkbox" id="enable-soundcork-proxy" onchange="updateProxySettings()"> Enable Soundcork Proxy (Legacy)</label>
|
||||
<label style="display: block; margin-bottom: 5px;">
|
||||
<input type="checkbox" id="proxy-record" onchange="updateProxySettings()"> Record Interactions
|
||||
<span style="font-size: 0.85em; color: #666; margin-left: 5px;">(View in <strong>5. Interactions</strong> tab)</span>
|
||||
|
||||
@@ -126,9 +126,6 @@ async function fetchSettings() {
|
||||
if (settings.server_url) {
|
||||
document.getElementById('target-domain').value = settings.server_url;
|
||||
}
|
||||
if (settings.proxy_url) {
|
||||
document.getElementById('soundcork-url').value = settings.proxy_url;
|
||||
}
|
||||
if (settings.discovery_interval) {
|
||||
document.getElementById('discovery-interval').value = settings.discovery_interval;
|
||||
}
|
||||
@@ -165,10 +162,6 @@ async function fetchSettings() {
|
||||
document.getElementById('internal-paths').value = settings.internal_paths.join('\n');
|
||||
}
|
||||
|
||||
if (settings.enable_soundcork_proxy !== undefined) {
|
||||
document.getElementById('enable-soundcork-proxy').checked = settings.enable_soundcork_proxy;
|
||||
}
|
||||
|
||||
const spotifyStatus = document.getElementById('spotify-config-status');
|
||||
if (spotifyStatus) {
|
||||
if (settings.spotify_configured) {
|
||||
@@ -193,9 +186,6 @@ async function fetchProxySettings() {
|
||||
document.getElementById('proxy-redact').checked = settings.redact;
|
||||
document.getElementById('proxy-log-body').checked = settings.log_body;
|
||||
document.getElementById('proxy-record').checked = settings.record;
|
||||
if (settings.enable_soundcork_proxy !== undefined) {
|
||||
document.getElementById('enable-soundcork-proxy').checked = settings.enable_soundcork_proxy;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch proxy settings', error);
|
||||
}
|
||||
@@ -206,7 +196,6 @@ async function updateProxySettings() {
|
||||
redact: document.getElementById('proxy-redact').checked,
|
||||
log_body: document.getElementById('proxy-log-body').checked,
|
||||
record: document.getElementById('proxy-record').checked,
|
||||
enable_soundcork_proxy: document.getElementById('enable-soundcork-proxy').checked
|
||||
};
|
||||
try {
|
||||
await fetch('/setup/proxy-settings', {
|
||||
@@ -222,7 +211,6 @@ async function updateProxySettings() {
|
||||
async function updateSettings() {
|
||||
const settings = {
|
||||
server_url: document.getElementById('target-domain').value,
|
||||
proxy_url: document.getElementById('soundcork-url').value,
|
||||
discovery_interval: document.getElementById('discovery-interval').value,
|
||||
discovery_enabled: document.getElementById('discovery-enabled').checked,
|
||||
dns_enabled: document.getElementById('dns-enabled').checked,
|
||||
@@ -232,7 +220,6 @@ async function updateSettings() {
|
||||
preferred_source: document.getElementById('preferred-source-upstream').checked ? 'upstream' : 'local',
|
||||
mirror_endpoints: document.getElementById('mirror-endpoints').value.split('\n').map(s => s.trim()).filter(s => s !== ''),
|
||||
internal_paths: document.getElementById('internal-paths').value.split('\n').map(s => s.trim()).filter(s => s !== ''),
|
||||
enable_soundcork_proxy: document.getElementById('enable-soundcork-proxy').checked
|
||||
};
|
||||
const status = document.getElementById('settings-status');
|
||||
status.innerText = 'Saving...';
|
||||
@@ -1068,7 +1055,6 @@ async function showSummary(deviceId) {
|
||||
return;
|
||||
}
|
||||
const targetUrl = document.getElementById('target-domain').value;
|
||||
const proxyUrl = document.getElementById('soundcork-url').value;
|
||||
|
||||
const opts = {
|
||||
marge: document.getElementById('opt-marge').value,
|
||||
@@ -1087,7 +1073,7 @@ async function showSummary(deviceId) {
|
||||
const outputBox = document.getElementById('command-output-box');
|
||||
if (outputBox) outputBox.style.display = 'none';
|
||||
|
||||
let query = '?target_url=' + encodeURIComponent(targetUrl) + '&proxy_url=' + encodeURIComponent(proxyUrl);
|
||||
let query = '?target_url=' + encodeURIComponent(targetUrl);
|
||||
for (let k in opts) {
|
||||
query += '&' + k + '=' + encodeURIComponent(opts[k]);
|
||||
}
|
||||
@@ -1334,7 +1320,6 @@ async function migrate(deviceId, ip) {
|
||||
return;
|
||||
}
|
||||
const targetUrl = document.getElementById('target-domain').value;
|
||||
const proxyUrl = document.getElementById('soundcork-url').value;
|
||||
const method = document.getElementById('migration-method').value;
|
||||
|
||||
const opts = {
|
||||
@@ -1353,7 +1338,7 @@ async function migrate(deviceId, ip) {
|
||||
const display = getDeviceDisplayName(deviceId);
|
||||
statusDiv.innerHTML = 'Migrating ' + display + ' using ' + method + '...';
|
||||
|
||||
let query = '?method=' + encodeURIComponent(method) + '&target_url=' + encodeURIComponent(targetUrl) + '&proxy_url=' + encodeURIComponent(proxyUrl);
|
||||
let query = '?method=' + encodeURIComponent(method) + '&target_url=' + encodeURIComponent(targetUrl);
|
||||
for (let k in opts) {
|
||||
query += '&' + k + '=' + encodeURIComponent(opts[k]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user