From 29463fac98c430436caea6fc90d8127ee4105887 Mon Sep 17 00:00:00 2001 From: Tobias Gesellchen Date: Sat, 8 Aug 2026 23:26:34 +0200 Subject: [PATCH] feat(admin): show the resolved data directory in Settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prompted by not being able to tell where a locally-run instance's data dir actually was without inspecting the running process (ps/lsof). Adds data_dir to /api/setup/version's response, resolved to an absolute path so it's unambiguous regardless of whether --data-dir/DATA_DIR was relative or left at the default. Shown as a read-only line at the top of the Settings tab, not the always- visible footer — the footer is prime real estate seen on every tab/every page load, and this is a rarely-needed piece of diagnostic info that belongs alongside the rest of System Settings instead. Also filled in a test-helper gap: the pkg/service/handlers package's internal test router (main_test.go) never registered /setup/version at all, unlike the real production router — added it so the new test (and any future one exercising this endpoint) can actually run. Unrelated to #419, but found while verifying that work against a running instance. --- pkg/service/handlers/handlers_setup.go | 15 ++++++++ pkg/service/handlers/handlers_setup_test.go | 40 +++++++++++++++++++++ pkg/service/handlers/main_test.go | 1 + pkg/service/handlers/web/index.html | 3 ++ pkg/service/handlers/web/js/script.js | 5 +++ 5 files changed, 64 insertions(+) diff --git a/pkg/service/handlers/handlers_setup.go b/pkg/service/handlers/handlers_setup.go index 81b3bb3..9460966 100644 --- a/pkg/service/handlers/handlers_setup.go +++ b/pkg/service/handlers/handlers_setup.go @@ -6,6 +6,7 @@ import ( "log" "net/http" "os" + "path/filepath" "sort" "strconv" "strings" @@ -1357,6 +1358,19 @@ func (s *Server) HandleGetVersionInfo(w http.ResponseWriter, _ *http.Request) { repoURL := s.RepoURL s.mu.RUnlock() + var dataDir string + + if s.ds != nil && s.ds.DataDir != "" { + // Resolve to absolute: the default ("data") and any relative + // --data-dir/DATA_DIR value are otherwise ambiguous without knowing + // the process's working directory at startup. + if abs, err := filepath.Abs(s.ds.DataDir); err == nil { + dataDir = abs + } else { + dataDir = s.ds.DataDir + } + } + w.Header().Set("Content-Type", "application/json") var ( @@ -1381,6 +1395,7 @@ func (s *Server) HandleGetVersionInfo(w http.ResponseWriter, _ *http.Request) { "repo_url": repoURL, "release_url": releaseURL, "commit_url": commitURL, + "data_dir": dataDir, }); 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 60d0273..3d16b68 100644 --- a/pkg/service/handlers/handlers_setup_test.go +++ b/pkg/service/handlers/handlers_setup_test.go @@ -443,6 +443,46 @@ func TestAdminAreaAuthRoundTrip(t *testing.T) { } } +// TestHandleGetVersionInfo_IncludesAbsoluteDataDir verifies /api/setup/version +// reports the actual data directory in use, resolved to an absolute path — +// added so operators running the service locally (not in Docker, where the +// path is obvious from the bind mount) can find it without having to +// inspect the running process. See NEXT.md/#419 session notes. +func TestHandleGetVersionInfo_IncludesAbsoluteDataDir(t *testing.T) { + tempDir, err := os.MkdirTemp("", "version-info-datadir-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]string + if err := json.NewDecoder(res.Body).Decode(&got); err != nil { + t.Fatalf("Failed to decode response: %v", err) + } + + wantAbs, err := filepath.Abs(tempDir) + if err != nil { + t.Fatalf("Failed to resolve expected absolute path: %v", err) + } + + if got["data_dir"] != wantAbs { + t.Errorf("Expected data_dir %q, got %q", wantAbs, got["data_dir"]) + } +} + func TestMigrationAndCA(t *testing.T) { tempDir, err := os.MkdirTemp("", "handlers-test") if err != nil { diff --git a/pkg/service/handlers/main_test.go b/pkg/service/handlers/main_test.go index d19e59d..5897c80 100644 --- a/pkg/service/handlers/main_test.go +++ b/pkg/service/handlers/main_test.go @@ -131,6 +131,7 @@ func setupRouter(targetURL string, ds *datastore.DataStore) (*chi.Mux, *Server) r.Delete("/devices/{deviceId}", server.HandleRemoveDevice) r.Get("/settings", server.HandleGetSettings) r.Post("/settings", server.HandleUpdateSettings) + r.Get("/version", server.HandleGetVersionInfo) r.Get("/logging-settings", server.HandleGetLoggingSettings) r.Post("/logging-settings", server.HandleUpdateLoggingSettings) r.Post("/ensure-remote-services/{deviceId}", server.HandleEnsureRemoteServices) diff --git a/pkg/service/handlers/web/index.html b/pkg/service/handlers/web/index.html index 7e17828..52b72de 100644 --- a/pkg/service/handlers/web/index.html +++ b/pkg/service/handlers/web/index.html @@ -171,6 +171,9 @@

System Settings

+

+ Data directory: - +

Service URLs

diff --git a/pkg/service/handlers/web/js/script.js b/pkg/service/handlers/web/js/script.js index ea91525..3f36e12 100644 --- a/pkg/service/handlers/web/js/script.js +++ b/pkg/service/handlers/web/js/script.js @@ -891,6 +891,11 @@ async function fetchVersion() { } info.innerHTML = `AfterTouch ${versionStr} (${commitStr}) • ${data.date}`; } + + const dataDirEl = document.getElementById("settings-data-dir"); + if (dataDirEl && data.data_dir) { + dataDirEl.textContent = data.data_dir; + } } catch (error) { console.error("Failed to fetch version info", error); }