From 2a072f9cc9c0337778ef5f174086a8386c1f824e Mon Sep 17 00:00:00 2001 From: kory Date: Fri, 2 Oct 2026 14:18:24 +0800 Subject: [PATCH] fix: detach local sessions from request context --- internal/api/api.go | 2 +- internal/api/api_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/internal/api/api.go b/internal/api/api.go index dce950c..3e25db2 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -851,7 +851,7 @@ func (s *Server) processSession(w http.ResponseWriter, r *http.Request) { } } var sess *executor.Session - sess, err = s.LocalSessions.Start(ctx, req.SessionID, req.Command, req.Workdir, owner, req.RingCapacity) + sess, err = s.LocalSessions.Start(context.Background(), req.SessionID, req.Command, req.Workdir, owner, req.RingCapacity) if err == nil { result = map[string]any{"session_id": sess.ID, "started_at": sess.StartedAt.Unix()} } diff --git a/internal/api/api_test.go b/internal/api/api_test.go index f58eb08..57df5d1 100644 --- a/internal/api/api_test.go +++ b/internal/api/api_test.go @@ -9,6 +9,7 @@ import ( "strings" "sync" "testing" + "time" "github.com/wjyhk/vps-commander/internal/executor" "github.com/wjyhk/vps-commander/internal/storage" @@ -92,6 +93,38 @@ func TestSecurityEffectiveModeAndExecGate(t *testing.T) { } } +func TestProcessSessionLocalSnakeCaseAndLifecycle(t *testing.T) { + dir := t.TempDir() + store, err := storage.Open(filepath.Join(dir, "db.sqlite")) + if err != nil { + t.Fatal(err) + } + defer store.DB.Close() + if err := store.SetGlobalSecurityMode("high"); err != nil { + t.Fatal(err) + } + s := &Server{Exec: executor.Local{MaxOutput: 1024 * 1024}, Store: store, LocalName: "local", LocalSessions: executor.NewSessionManager(0)} + if err := s.LoadSecurityPolicySnapshot(); err != nil { + t.Fatal(err) + } + req := httptest.NewRequest(http.MethodPost, "/api/v1/process/session?action=start", strings.NewReader(`{"device":"local","session_id":"session-test-001","command":"sleep 0.2; printf local-ok"}`)) + w := httptest.NewRecorder() + s.processSession(w, req) + if w.Code != http.StatusOK { + t.Fatalf("start status=%d body=%s", w.Code, w.Body.String()) + } + time.Sleep(400 * time.Millisecond) + req = httptest.NewRequest(http.MethodPost, "/api/v1/process/session?action=read", strings.NewReader(`{"device":"local","session_id":"session-test-001"}`)) + w = httptest.NewRecorder() + s.processSession(w, req) + if w.Code != http.StatusOK { + t.Fatalf("read status=%d body=%s", w.Code, w.Body.String()) + } + if !strings.Contains(w.Body.String(), "local-ok") { + t.Fatalf("missing session output: %s", w.Body.String()) + } +} + func TestEditBlockSecurityAndReplacement(t *testing.T) { dir := t.TempDir() store, err := storage.Open(filepath.Join(dir, "db.sqlite"))