Choose the hub: Settings' choice, else the install's channel's; state kept per hub
CI / build (push) Successful in 18s

The client's Settings > T5DE chooses the hub from the next start on (POST /hub/server, kept in server.json);
without a choice, -channel (which the client passes) decides: staging for nightly and dev, T5DE's hub for
releases. $T5DE_HUB still wins. Each hub's sign-in, product IDs and products are kept in their own folder
(T5DE's hub in the data folder as before, others in hubs/<host>), so a hub never gets another's token and
product IDs from two hubs never clash; a sign-in saved for another hub is ignored. Signing in always goes to
this run's hub.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
dhkatzandClaude Opus 5.5 committed 2026-11-02 09:09:34 -08:00
1 parent 8daf91ede3
commit 8ae45fc07a
6 files changed
+292 -53

No files matched your search

+5 -17
View File
@@ -94,7 +94,7 @@ func TestSignInAndOut(t *testing.T) {
path := filepath.Join(t.TempDir(), "hub.json")
var opened []string
attached := 0
m := &Manager{Path: path, DefaultHub: srv.URL, Log: log.New(io.Discard, "", 0),
m := &Manager{Path: path, Hub: srv.URL, Default: srv.URL, Log: log.New(io.Discard, "", 0),
OpenBrowser: func(p string) { opened = append(opened, p) }, Attach: func(*hub.Client) { attached++ }}
m.Start()
if s := ask(t, m, "GET", "/hub", ""); s.SignedIn || s.Hub != srv.URL {
@@ -122,22 +122,10 @@ func TestSignInAndOut(t *testing.T) {
if _, err := os.Stat(path); !os.IsNotExist(err) {
t.Fatalf("hub.json: %v", err)
}
// Signing in again goes to the same hub.
m.DefaultHub = "https://elsewhere.test"
if s := ask(t, m, "GET", "/hub", ""); s.Hub != srv.URL {
t.Fatalf("hub after signing out: %q", s.Hub)
}
// Only https hubs (or this computer's); other requests to the proxy's address are refused.
u, _ := url.Parse("http://" + Host + "/hub/signin")
// Other requests to the proxy's address are refused.
u, _ := url.Parse("http://" + Host + "/other")
req, _ := http.NewRequest("POST", u.String(), nil)
x := &proxy.Exchange{URL: u, Request: req, Body: []byte("hub=http://evil.test")}
m.Intercept(x)
if x.Response.StatusCode != 400 {
t.Fatalf("plain http hub: %d", x.Response.StatusCode)
}
u, _ = url.Parse("http://" + Host + "/other")
x = &proxy.Exchange{URL: u, Request: req}
x := &proxy.Exchange{URL: u, Request: req}
m.Intercept(x)
if x.Response.StatusCode != 404 {
t.Fatalf("other path: %d", x.Response.StatusCode)
@@ -154,7 +142,7 @@ func TestSignInAndOut(t *testing.T) {
func TestBadges(t *testing.T) {
var signedOut atomic.Bool
srv := fakeHub(t, &signedOut)
m := &Manager{Path: filepath.Join(t.TempDir(), "hub.json"), DefaultHub: srv.URL, Log: log.New(io.Discard, "", 0)}
m := &Manager{Path: filepath.Join(t.TempDir(), "hub.json"), Hub: srv.URL, Log: log.New(io.Discard, "", 0)}
badges := func(body string) string {
t.Helper()
u, _ := url.Parse("http://" + Host + "/badges")