feat(server): /healthz returns min_client_version

Lets the M7 Flutter client refuse to operate against an incompatible
server (e.g., new endpoints the client depends on, breaking schema
shifts). The version constant is bumped on each release where the
client/server contract changes; old clients show the user a blocking
"update required" modal pointed at the latest APK / TestFlight build.
This commit is contained in:
2026-05-02 15:11:10 -04:00
parent c3be0f3e6e
commit b03da903d9
3 changed files with 37 additions and 1 deletions
+4 -1
View File
@@ -103,7 +103,10 @@ func (s *Server) Router() http.Handler {
func (s *Server) handleHealthz(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
_ = json.NewEncoder(w).Encode(map[string]string{
"status": "ok",
"min_client_version": MinClientVersion,
})
}
// handleAdminScan runs a scan synchronously and returns the resulting stats.
+27
View File
@@ -11,6 +11,7 @@ import (
"strings"
"testing"
"github.com/go-chi/chi/v5"
"github.com/jackc/pgx/v5/pgxpool"
"git.fabledsword.com/bvandeusen/minstrel/internal/auth"
@@ -45,6 +46,32 @@ func TestHealthz(t *testing.T) {
}
}
func TestHealthz_IncludesMinClientVersion(t *testing.T) {
t.Parallel()
s := &Server{}
r := chi.NewRouter()
r.Get("/healthz", s.handleHealthz)
ts := httptest.NewServer(r)
defer ts.Close()
resp, err := http.Get(ts.URL + "/healthz")
if err != nil {
t.Fatalf("GET /healthz: %v", err)
}
defer resp.Body.Close()
var body map[string]string
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
t.Fatalf("decode: %v", err)
}
if body["status"] != "ok" {
t.Errorf("status: got %q want \"ok\"", body["status"])
}
if body["min_client_version"] == "" {
t.Error("min_client_version is empty; clients can't enforce pairing")
}
}
func TestRouter_ServesSPAAtRoot(t *testing.T) {
s := New(slog.New(slog.NewTextHandler(io.Discard, nil)), nil, nil, subsonic.Config{}, config.EventsConfig{}, config.RecommendationConfig{})
ts := httptest.NewServer(s.Router())
+6
View File
@@ -0,0 +1,6 @@
package server
// MinClientVersion is the lowest mobile-client semver that this server
// accepts. Bump it when a server change requires a paired client update;
// older clients see version_too_old at /healthz and refuse to operate.
const MinClientVersion = "0.1.0"