feat(admin): show the resolved data directory in Settings

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.
This commit is contained in:
Tobias Gesellchen
2026-08-08 23:49:57 +02:00
parent bdcbd29e3b
commit 29463fac98
5 changed files with 64 additions and 0 deletions
+15
View File
@@ -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
@@ -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 {
+1
View File
@@ -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)
+3
View File
@@ -171,6 +171,9 @@
<!-- Tab 1: Settings -->
<div id="tab-settings" class="tab-content">
<h2>System Settings</h2>
<p style="font-size: 0.85em; color: #666; margin: -8px 0 20px 0;">
Data directory: <code id="settings-data-dir">-</code>
</p>
<div style="margin-bottom: 20px">
<strong>Service URLs</strong>
<p style="font-size: 0.9em; color: #555; margin: 6px 0 12px 0">
+5
View File
@@ -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);
}