diff --git a/docs/content/docs/guides/SOUNDTOUCH-SERVICE.md b/docs/content/docs/guides/SOUNDTOUCH-SERVICE.md index 69a7464..19b2817 100644 --- a/docs/content/docs/guides/SOUNDTOUCH-SERVICE.md +++ b/docs/content/docs/guides/SOUNDTOUCH-SERVICE.md @@ -156,30 +156,32 @@ The service supports multiple ways to configure its behavior. When multiple sour ### Configuration Options -| Variable | Flag | Description | Default | -|------------------------------------|----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------| -| `PORT` | `--port`, `-p` | HTTP port to bind the service to | `8000` | -| `BIND_ADDR` | `--bind` | Network interface to bind to | all (ipv4 and ipv6) | -| `DATA_DIR` | `--data-dir` | Directory for persistent data | `./data` | -| `SERVER_URL` | `--server-url`, `-s` | External URL of this service | `http://:8000` | -| `HTTPS_PORT` | `--https-port` | HTTPS port to bind the service to | `8443` | -| `HTTPS_SERVER_URL` | `--https-server-url`, `-S` | External HTTPS URL. An override: when empty it is derived from `SERVER_URL` (same host, `https`, on `HTTPS_PORT`), and can also be viewed/overridden in Settings. | derived from `SERVER_URL` | -| `PYTHON_BACKEND_URL`, `TARGET_URL` | `--target-url` | URL for Python-based service components (legacy) | `http://localhost:8001` | -| `REDACT_PROXY_LOGS` | `--redact-logs` | Redact sensitive data in proxy logs | `true` | -| `LOG_PROXY_BODY` | `--log-bodies` | Log full request/response bodies | `false` | -| `RECORD_INTERACTIONS` | `--record-interactions` | Record HTTP interactions to disk | `true` | -| `DISCOVERY_INTERVAL` | `--discovery-interval` | Device discovery interval | `5m` | -| `ENABLE_DNS_DISCOVERY` | `--dns-discovery` | Enable DNS discovery server | `false` | -| `DNS_UPSTREAM` | `--dns-upstream` | Upstream DNS server for non-Bose queries | `8.8.8.8` | -| `DNS_BIND_ADDR` | `--dns-bind` | Bind address for the DNS discovery server (standard port `:53` is required for DNS/DHCP migration) | `:53` | -| `INTERNAL_PATHS` | `--internal-paths` | Paths for internal requests to exclude from recording (e.g., `/setup/*`, `/web/*`) | `[]` | -| `DISCOVERY_DISABLED` | | Disable automated device discovery | `false` | -| `MGMT_USERNAME` | `--mgmt-username` | Username for HTTP Basic Auth on the Management API (`/api/mgmt/*`, `/mgmt/*`) — Spotify/Amazon account linking, Local Accounts | `admin` | -| `MGMT_PASSWORD` | `--mgmt-password` | Password for the same Management API Basic Auth. **Change this if AfterTouch is reachable beyond a trusted LAN** — the default is published in this doc. | `change_me!` | -| `STOCKHOLM_DIR` | `--stockholm-dir` | Path to extracted Stockholm frontend directory — enables the Stockholm UI when set | *(disabled)* | -| `MARGE_URL` | | Streaming/marge base URL used when rewriting `stockholm/json/config.json`. Defaults to `SERVER_URL`. Set to `SERVER_URL/marge` only when using a soundcork backend. | *(same as `SERVER_URL`)* | -| `MARGE_AUTH_TOKEN` | | Pre-seeds the Stockholm `margeAuthToken` state (skips the login step for the first session) | *(empty)* | -| `MARGE_ACCOUNT_ID` | | Pre-seeds the Stockholm `margeAccountID` state (used to filter device-discovery results by account) | *(empty)* | +| Variable | Flag | Description | Default | +|------------------------------------|----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------| +| `PORT` | `--port`, `-p` | HTTP port to bind the service to | `8000` | +| `BIND_ADDR` | `--bind` | Network interface to bind to | all (ipv4 and ipv6) | +| `DATA_DIR` | `--data-dir` | Directory for persistent data | `./data` | +| `SERVER_URL` | `--server-url`, `-s` | External URL of this service | `http://:8000` | +| `HTTPS_PORT` | `--https-port` | HTTPS port to bind the service to | `8443` | +| `HTTPS_SERVER_URL` | `--https-server-url`, `-S` | External HTTPS URL. An override: when empty it is derived from `SERVER_URL` (same host, `https`, on `HTTPS_PORT`), and can also be viewed/overridden in Settings. | derived from `SERVER_URL` | +| `PYTHON_BACKEND_URL`, `TARGET_URL` | `--target-url` | URL for Python-based service components (legacy) | `http://localhost:8001` | +| `REDACT_PROXY_LOGS` | `--redact-logs` | Redact sensitive data in proxy logs | `true` | +| `LOG_PROXY_BODY` | `--log-bodies` | Log full request/response bodies | `false` | +| `RECORD_INTERACTIONS` | `--record-interactions` | Record HTTP interactions to disk | `true` | +| `DISCOVERY_INTERVAL` | `--discovery-interval` | Device discovery interval | `5m` | +| `ENABLE_DNS_DISCOVERY` | `--dns-discovery` | Enable DNS discovery server | `false` | +| `DNS_UPSTREAM` | `--dns-upstream` | Upstream DNS server for non-Bose queries | `8.8.8.8` | +| `DNS_BIND_ADDR` | `--dns-bind` | Bind address for the DNS discovery server (standard port `:53` is required for DNS/DHCP migration) | `:53` | +| `INTERNAL_PATHS` | `--internal-paths` | Paths for internal requests to exclude from recording (e.g., `/setup/*`, `/web/*`) | `[]` | +| `DISCOVERY_DISABLED` | | Disable automated device discovery | `false` | +| `UPDATE_CHECK_ENABLED` | `--update-check-enabled` | Periodically check GitHub Releases for a newer version and show a dismissible notice in the admin UI and Player when one is found. **Opt-in**: this is the only network call AfterTouch makes beyond speaker/provider traffic when enabled, so it defaults off. One unauthenticated `GET` per interval to `api.github.com`, nothing else leaves the box. | `false` | +| `UPDATE_CHECK_INTERVAL` | `--update-check-interval` | Update check interval | `24h` | +| `MGMT_USERNAME` | `--mgmt-username` | Username for HTTP Basic Auth on the Management API (`/api/mgmt/*`, `/mgmt/*`) — Spotify/Amazon account linking, Local Accounts | `admin` | +| `MGMT_PASSWORD` | `--mgmt-password` | Password for the same Management API Basic Auth. **Change this if AfterTouch is reachable beyond a trusted LAN** — the default is published in this doc. | `change_me!` | +| `STOCKHOLM_DIR` | `--stockholm-dir` | Path to extracted Stockholm frontend directory — enables the Stockholm UI when set | *(disabled)* | +| `MARGE_URL` | | Streaming/marge base URL used when rewriting `stockholm/json/config.json`. Defaults to `SERVER_URL`. Set to `SERVER_URL/marge` only when using a soundcork backend. | *(same as `SERVER_URL`)* | +| `MARGE_AUTH_TOKEN` | | Pre-seeds the Stockholm `margeAuthToken` state (skips the login step for the first session) | *(empty)* | +| `MARGE_ACCOUNT_ID` | | Pre-seeds the Stockholm `margeAccountID` state (used to filter device-discovery results by account) | *(empty)* | ### Configuration Examples diff --git a/pkg/service/handlers/handlers_setup.go b/pkg/service/handlers/handlers_setup.go index 9460966..aaf409e 100644 --- a/pkg/service/handlers/handlers_setup.go +++ b/pkg/service/handlers/handlers_setup.go @@ -1388,14 +1388,22 @@ func (s *Server) HandleGetVersionInfo(w http.ResponseWriter, _ *http.Request) { releaseURL = fmt.Sprintf("%s/releases/tag/%s", repoURL, version) } - if err := json.NewEncoder(w).Encode(map[string]string{ - "version": version, - "commit": commit, - "date": date, - "repo_url": repoURL, - "release_url": releaseURL, - "commit_url": commitURL, - "data_dir": dataDir, + // Opt-in periodic update check (#591) — UpdateCheckResult is nil-safe and + // returns the zero value (Available: false) when the check was never + // enabled, which is the common case. + updateCheck := s.UpdateCheckResult() + + if err := json.NewEncoder(w).Encode(map[string]interface{}{ + "version": version, + "commit": commit, + "date": date, + "repo_url": repoURL, + "release_url": releaseURL, + "commit_url": commitURL, + "data_dir": dataDir, + "update_available": updateCheck.Available, + "latest_version": updateCheck.LatestVersion, + "latest_release_url": updateCheck.ReleaseURL, }); err != nil { http.Error(w, "Failed to encode response", http.StatusInternalServerError) return diff --git a/pkg/service/handlers/handlers_setup_test.go b/pkg/service/handlers/handlers_setup_test.go index 3d16b68..55242f3 100644 --- a/pkg/service/handlers/handlers_setup_test.go +++ b/pkg/service/handlers/handlers_setup_test.go @@ -468,7 +468,7 @@ func TestHandleGetVersionInfo_IncludesAbsoluteDataDir(t *testing.T) { } defer res.Body.Close() - var got map[string]string + var got map[string]interface{} if err := json.NewDecoder(res.Body).Decode(&got); err != nil { t.Fatalf("Failed to decode response: %v", err) } @@ -483,6 +483,45 @@ func TestHandleGetVersionInfo_IncludesAbsoluteDataDir(t *testing.T) { } } +// TestHandleGetVersionInfo_UpdateCheckFields verifies the #591 fields are +// present and reflect a nil-checker default (Available: false) when the +// update check was never enabled — the common case. +func TestHandleGetVersionInfo_UpdateCheckFields(t *testing.T) { + tempDir, err := os.MkdirTemp("", "version-info-updatecheck-test") + if err != nil { + t.Fatalf("Failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + ds := datastore.NewDataStore(tempDir) + _ = ds.Initialize() + + r, _ := setupRouter("http://127.0.0.1:8000", ds) + ts := httptest.NewServer(r) + defer ts.Close() + + res, err := http.Get(ts.URL + "/setup/version") + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + + var got map[string]interface{} + if err := json.NewDecoder(res.Body).Decode(&got); err != nil { + t.Fatalf("Failed to decode response: %v", err) + } + + if got["update_available"] != false { + t.Errorf("Expected update_available=false by default, got %v", got["update_available"]) + } + if _, ok := got["latest_version"]; !ok { + t.Error("Expected a latest_version key in the response") + } + if _, ok := got["latest_release_url"]; !ok { + t.Error("Expected a latest_release_url key in the response") + } +} + func TestMigrationAndCA(t *testing.T) { tempDir, err := os.MkdirTemp("", "handlers-test") if err != nil {