fix(tts): move TTS endpoints from /mgmt to /setup (no Basic Auth)

The TTS speak/config endpoints were under /mgmt (Basic-Auth protected),
but the soundtouch-web proxy and CLI authenticated with their own
mgmt-password default (empty) while the service defaults to "change_me!",
so speaking from -web returned 401.

This was also inconsistent: the Google API key is configured via the
unauthenticated /setup/settings, and Play URL already proxies to /setup,
so gating only TTS playback behind mgmt auth made no sense. Move
/mgmt/tts/{speak,config} to /setup/tts/{speak,config} (LAN-trust, like
the rest of the setup surface), rename the handlers accordingly, and drop
the now-unused mgmt-credential plumbing from soundtouch-web and the CLI
tts command.

Verified: POST /setup/tts/speak now reaches the handler without auth
(502 only because the test speaker IP is unreachable; previously 401).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tobias Gesellchen
2026-05-31 22:35:31 +02:00
co-authored by Claude Opus 4.8
parent 8f2939a9a6
commit 169c1c5b9f
10 changed files with 31 additions and 64 deletions
+1 -13
View File
@@ -65,17 +65,6 @@ func ttsSpeakCmd() *cli.Command {
Aliases: []string{"v"},
Usage: "Playback volume (0-100, 0 = service default)",
},
&cli.StringFlag{
Name: "mgmt-username",
Usage: "Management API username for HTTP Basic Auth",
Value: "admin",
EnvVars: []string{"MGMT_USERNAME"},
},
&cli.StringFlag{
Name: "mgmt-password",
Usage: "Management API password for HTTP Basic Auth",
EnvVars: []string{"MGMT_PASSWORD"},
},
),
Action: ttsSpeak,
}
@@ -116,13 +105,12 @@ func ttsSpeak(c *cli.Context) error {
return fmt.Errorf("marshal request: %w", err)
}
req, err := http.NewRequest(http.MethodPost, serviceURL+"/mgmt/tts/speak", bytes.NewReader(body))
req, err := http.NewRequest(http.MethodPost, serviceURL+"/setup/tts/speak", bytes.NewReader(body))
if err != nil {
return fmt.Errorf("build request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.SetBasicAuth(c.String("mgmt-username"), c.String("mgmt-password"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
+5 -5
View File
@@ -1321,11 +1321,6 @@ func setupRouter(server *handlers.Server, stockholmHandler *stockholm.Handler) *
r.Post("/prime", server.HandleMgmtPrimeDeviceAmazon)
})
r.Route("/tts", func(r chi.Router) {
r.Post("/speak", server.HandleMgmtTTSSpeak)
r.Get("/config", server.HandleMgmtTTSConfig)
})
r.Get("/devices/{deviceId}/events", server.HandleMgmtDeviceEvents)
})
})
@@ -1338,6 +1333,11 @@ func setupRouter(server *handlers.Server, stockholmHandler *stockholm.Handler) *
r.Get("/discovery-status", server.HandleGetDiscoveryStatus)
r.Get("/settings", server.HandleGetSettings)
r.Post("/settings", server.HandleUpdateSettings)
// TTS lives under /setup (LAN-trust, like the rest of the integration
// surface and Play URL), not /mgmt: the API key is already configured
// via /setup/settings, and -web/CLI reach this without mgmt credentials.
r.Post("/tts/speak", server.HandleTTSSpeak)
r.Get("/tts/config", server.HandleTTSConfig)
r.Get("/info/{deviceId}", server.HandleGetDeviceInfo)
r.Get("/summary/{deviceId}", server.HandleGetMigrationSummary)
r.Post("/migrate/{deviceId}", server.HandleMigrateDevice)
+2 -2
View File
@@ -59,7 +59,6 @@ GET /mgmt/devices/{deviceId}/events handlers.(
GET /mgmt/spotify/accounts handlers.(*Server).HandleMgmtSpotifyAccounts-fm
GET /mgmt/spotify/callback handlers.(*Server).HandleMgmtSpotifyCallback-fm
GET /mgmt/spotify/token handlers.(*Server).HandleMgmtSpotifyToken-fm
GET /mgmt/tts/config handlers.(*Server).HandleMgmtTTSConfig-fm
GET /setup/account-id-suggestions/{deviceId} handlers.(*Server).HandleAccountIDSuggestions-fm
GET /setup/ca.crt handlers.(*Server).HandleGetCACert-fm
GET /setup/device-summary/{deviceId} handlers.(*Server).HandleDeviceSummary-fm
@@ -79,6 +78,7 @@ GET /setup/logging-settings handlers.(
GET /setup/logs handlers.(*Server).HandleGetLogs-fm
GET /setup/settings handlers.(*Server).HandleGetSettings-fm
GET /setup/summary/{deviceId} handlers.(*Server).HandleGetMigrationSummary-fm
GET /setup/tts/config handlers.(*Server).HandleTTSConfig-fm
GET /setup/version handlers.(*Server).HandleGetVersionInfo-fm
GET /streaming/account/{account}/device/{device}/group handlers.(*Server).HandleMargeDeviceGroup-fm
GET /streaming/account/{account}/device/{device}/group/ handlers.(*Server).HandleMargeDeviceGroup-fm
@@ -132,7 +132,6 @@ POST /mgmt/spotify/confirm handlers.(
POST /mgmt/spotify/entity handlers.(*Server).HandleMgmtSpotifyEntity-fm
POST /mgmt/spotify/init handlers.(*Server).HandleMgmtSpotifyInit-fm
POST /mgmt/spotify/prime handlers.(*Server).HandleMgmtPrimeDevice-fm
POST /mgmt/tts/speak handlers.(*Server).HandleMgmtTTSSpeak-fm
POST /oauth/account/{account}/music/musicprovider/{sourceID}/token/cs handlers.(*Server).HandleBoseAccountToken-fm
POST /oauth/device/{deviceID}/music/musicprovider/{sourceID}/token handlers.(*Server).HandleBoseLegacyToken-fm
POST /oauth/device/{deviceID}/music/musicprovider/{sourceID}/token/cs1 handlers.(*Server).HandleBoseToken-fm
@@ -155,6 +154,7 @@ POST /setup/test-connection/{deviceId} handlers.(
POST /setup/test-dns/{deviceId} handlers.(*Server).HandleTestDNSRedirection-fm
POST /setup/test-hosts/{deviceId} handlers.(*Server).HandleTestHostsRedirection-fm
POST /setup/trust-ca/{deviceId} handlers.(*Server).HandleTrustCACert-fm
POST /setup/tts/speak handlers.(*Server).HandleTTSSpeak-fm
POST /streaming/account handlers.(*Server).HandleMargeCreateAccount-fm
POST /streaming/account/login handlers.(*Server).HandleMargeLogin-fm
POST /streaming/account/{account}/device/ handlers.(*Server).HandleMargeAddDevice-fm
-13
View File
@@ -81,17 +81,6 @@ func main() {
Usage: "AfterTouch service base URL (e.g. https://soundtouch.local). Required for custom stream URLs to work as presets via LOCAL_INTERNET_RADIO",
EnvVars: []string{"SERVICE_URL"},
},
&cli.StringFlag{
Name: "mgmt-username",
Usage: "AfterTouch management API username, used to proxy TTS to the service's /mgmt endpoints",
Value: "admin",
EnvVars: []string{"MGMT_USERNAME"},
},
&cli.StringFlag{
Name: "mgmt-password",
Usage: "AfterTouch management API password, used to proxy TTS to the service's /mgmt endpoints",
EnvVars: []string{"MGMT_PASSWORD"},
},
},
Action: func(c *cli.Context) error {
port := c.String("port")
@@ -126,8 +115,6 @@ func main() {
webApp.Date = date
webApp.RepoURL = repoURL
webApp.ServiceURL = strings.TrimRight(c.String("service-url"), "/")
webApp.MgmtUsername = c.String("mgmt-username")
webApp.MgmtPassword = c.String("mgmt-password")
discoveryService := soundtouchweb.NewDiscoveryService(ifaceName)