mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
fix(studio): register file backups on machine settings routes
Problem: local settings cannot load the file-backup catalog because both machine settings URL forms return an untyped 404. Root cause: file-backup handlers are registered only with account backup routes, while the machine settings router registers a separate route set. Fix: register the existing file-backup handlers with machine settings. Normal runtime routes still register that same set once. Preserve host-only checks, paired-device rejection, local-owner selection and mutation gates; do not change payloads, encryption or persisted formats. Verification: new regressions fail before the fix with plain 404 responses and pass afterward for both machine prefixes. Focused machine settings, file backup, account backup and mutation-gate tests pass. gofmt, go vet, make lint, make check and the required builtin/boot tests pass.
This commit is contained in:
1 parent
4377886f88
commit
6881c9748d
3 files changed
+101
-1
No files matched your search
@@ -25,7 +25,6 @@ func (s *Server) registerBackupRoutes(mux *http.ServeMux) {
|
||||
mux.HandleFunc("DELETE /backups/{id}", s.deleteBackup)
|
||||
mux.HandleFunc("POST /backups/{id}/preview", s.previewBackup)
|
||||
mux.HandleFunc("POST /backups/apply", s.applyBackup)
|
||||
s.registerBackupFileRoutes(mux)
|
||||
}
|
||||
|
||||
// backupSession is the gate every backup route shares: the host must allow
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package serve
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"reasonix/internal/base/testenv"
|
||||
)
|
||||
|
||||
func TestMachineSettingsFileBackupRoutes(t *testing.T) {
|
||||
t.Setenv("REASONIX_HOME", testenv.TempDir(t))
|
||||
h := NewHub(HubOptions{})
|
||||
defer h.Shutdown()
|
||||
rt := hubRuntime(t, h, testenv.TempDir(t))
|
||||
handler := operatorHandler(h)
|
||||
for _, base := range []string{"/host/settings", "/host/settings/runtime/" + rt.ID} {
|
||||
t.Run(base, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
handler.ServeHTTP(w, httptest.NewRequest(http.MethodGet, base+"/backups/file", nil))
|
||||
var catalog struct {
|
||||
Categories []json.RawMessage `json:"categories"`
|
||||
MinPassphrase int `json:"minPassphrase"`
|
||||
}
|
||||
if w.Code != http.StatusOK || json.Unmarshal(w.Body.Bytes(), &catalog) != nil || len(catalog.Categories) == 0 || catalog.MinPassphrase == 0 {
|
||||
t.Fatalf("catalog = %d %s", w.Code, w.Body.String())
|
||||
}
|
||||
for _, tc := range []struct {
|
||||
path, body string
|
||||
status int
|
||||
}{
|
||||
{"/backups/file/export", `{`, http.StatusBadRequest},
|
||||
{"/backups/file/preview", `{"file":"!!","passphrase":"test"}`, http.StatusUnprocessableEntity},
|
||||
{"/backups/file/apply", `{"planId":"missing","items":["x"]}`, http.StatusGone},
|
||||
} {
|
||||
r := httptest.NewRequest(http.MethodPost, base+tc.path, strings.NewReader(tc.body))
|
||||
r.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
handler.ServeHTTP(w, r)
|
||||
if w.Code != tc.status {
|
||||
t.Fatalf("%s = %d %s, want %d", tc.path, w.Code, w.Body.String(), tc.status)
|
||||
}
|
||||
unauthorized := httptest.NewRequest(http.MethodPost, "http://localhost"+base+tc.path, strings.NewReader(tc.body))
|
||||
unauthorized.Header.Set("Content-Type", "application/json")
|
||||
denied := httptest.NewRecorder()
|
||||
h.Handler().ServeHTTP(denied, unauthorized)
|
||||
if denied.Code != http.StatusForbidden {
|
||||
t.Fatalf("unauthorized %s = %d %s", base+tc.path, denied.Code, denied.Body.String())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMachineSettingsFileBackupRefusesPairedDevices(t *testing.T) {
|
||||
t.Setenv("REASONIX_HOME", testenv.TempDir(t))
|
||||
h := NewHub(HubOptions{})
|
||||
defer h.Shutdown()
|
||||
rt := hubRuntime(t, h, testenv.TempDir(t))
|
||||
for _, base := range []string{"/host/settings", "/host/settings/runtime/" + rt.ID} {
|
||||
for _, tc := range []struct{ method, path string }{
|
||||
{http.MethodGet, "/backups/file"},
|
||||
{http.MethodPost, "/backups/file/export"},
|
||||
{http.MethodPost, "/backups/file/preview"},
|
||||
{http.MethodPost, "/backups/file/apply"},
|
||||
} {
|
||||
r := httptest.NewRequest(tc.method, base+tc.path, strings.NewReader(`{}`))
|
||||
r.Header.Set("Content-Type", "application/json")
|
||||
r = r.WithContext(withDeviceReach(r.Context(), "phone", 1))
|
||||
w := httptest.NewRecorder()
|
||||
operatorHandler(h).ServeHTTP(w, r)
|
||||
var refusal struct{ Code string }
|
||||
if w.Code != http.StatusNotFound || json.Unmarshal(w.Body.Bytes(), &refusal) != nil || refusal.Code != codeDeviceHostOnly {
|
||||
t.Fatalf("device %s = %d %s", base+tc.path, w.Code, w.Body.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMachineSettingsFileBackupRequiresLocalOwner(t *testing.T) {
|
||||
t.Setenv("REASONIX_HOME", testenv.TempDir(t))
|
||||
h := NewHub(HubOptions{})
|
||||
defer h.Shutdown()
|
||||
remote := remotePane(t, h, fakeRemoteKernel(t).Listener.Addr().String())
|
||||
request := func(path string) {
|
||||
t.Helper()
|
||||
w := httptest.NewRecorder()
|
||||
operatorHandler(h).ServeHTTP(w, httptest.NewRequest(http.MethodGet, path, nil))
|
||||
var refusal struct{ Code string }
|
||||
if w.Code != http.StatusServiceUnavailable || json.Unmarshal(w.Body.Bytes(), &refusal) != nil || refusal.Code != "settings.local_runtime_required" {
|
||||
t.Fatalf("%s = %d %s", path, w.Code, w.Body.String())
|
||||
}
|
||||
}
|
||||
request("/host/settings/backups/file")
|
||||
hubRuntime(t, h, testenv.TempDir(t))
|
||||
request("/host/settings/runtime/" + remote.ID + "/backups/file")
|
||||
request("/host/settings/runtime/missing/backups/file")
|
||||
}
|
||||
@@ -14,6 +14,7 @@ func (s *Server) registerMachineSettingsRoutes(mux *http.ServeMux) {
|
||||
s.registerTurnNarrationRoutes(mux)
|
||||
s.registerWebAccessRoutes(mux)
|
||||
s.registerReceiptRoutes(mux)
|
||||
s.registerBackupFileRoutes(mux)
|
||||
mux.HandleFunc("GET /models", s.models)
|
||||
mux.HandleFunc("GET /memory", s.memories)
|
||||
mux.HandleFunc("POST /memory/forget", s.forgetMemory)
|
||||
|
||||
Reference in new issue
Block a user