From f77f075745bc20e00ebca6a0239a46f5dcc9a042 Mon Sep 17 00:00:00 2001 From: turnercore Date: Sun, 13 Sep 2026 16:55:42 +0200 Subject: [PATCH] fix(quota): avoid core management route collision --- internal/plugin/plugin.go | 2 +- internal/plugin/quota.go | 2 +- internal/plugin/quota_test.go | 10 +++++----- resources/quota_page.html | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/plugin/plugin.go b/internal/plugin/plugin.go index 86ac04e..b4d7933 100644 --- a/internal/plugin/plugin.go +++ b/internal/plugin/plugin.go @@ -214,7 +214,7 @@ func (m *Manager) registerManagement(request []byte) ([]byte, error) { Routes: []struct { Method string `json:"method"` Path string `json:"path"` - }{{Method: "POST", Path: "/plugins/" + pluginName + "/quota"}}, + }{{Method: "POST", Path: "/plugins/" + pluginName + "/quota-usage"}}, Resources: []struct { Path string `json:"path"` Menu string `json:"menu"` diff --git a/internal/plugin/quota.go b/internal/plugin/quota.go index 80110a1..cf069f4 100644 --- a/internal/plugin/quota.go +++ b/internal/plugin/quota.go @@ -62,7 +62,7 @@ func (m *Manager) HandleManagement(ctx context.Context, req pluginapi.Management if req.Method == http.MethodGet && req.Path == "/v0/resource/plugins/"+pluginName+"/quota" { return pluginapi.ManagementResponse{Headers: http.Header{"Content-Type": []string{"text/html; charset=utf-8"}}, Body: []byte(resources.QuotaPage)}, nil } - if req.Method != http.MethodPost || req.Path != "/v0/management/plugins/"+pluginName+"/quota" { + if req.Method != http.MethodPost || req.Path != "/v0/management/plugins/"+pluginName+"/quota-usage" { return pluginapi.ManagementResponse{StatusCode: http.StatusNotFound, Body: []byte(`{"error":"not found"}`)}, nil } var body quotaRequest diff --git a/internal/plugin/quota_test.go b/internal/plugin/quota_test.go index 7d9484e..f6206b7 100644 --- a/internal/plugin/quota_test.go +++ b/internal/plugin/quota_test.go @@ -21,7 +21,7 @@ func TestManagementRegistration(t *testing.T) { Resources []struct{ Path, Menu, Description string } `json:"resources"` } decodeResult(t, mustHandle(t, m, pluginabi.MethodManagementRegister, []byte(`{}`)), &got) - if len(got.Routes) != 1 || got.Routes[0].Method != http.MethodPost || got.Routes[0].Path != "/plugins/"+pluginName+"/quota" { + if len(got.Routes) != 1 || got.Routes[0].Method != http.MethodPost || got.Routes[0].Path != "/plugins/"+pluginName+"/quota-usage" { t.Fatalf("routes = %+v", got.Routes) } if len(got.Resources) != 1 || got.Resources[0].Path != "/quota" || got.Resources[0].Menu != "OpenCode Go Quota" { @@ -39,7 +39,7 @@ func TestQuotaListDoesNotCallHost(t *testing.T) { m := NewManager(NewHostBridge(f.call)) m.cfg = config.Config{APIKeys: []config.APIKey{{Value: "quota-key-a"}, {Value: "quota-key-b"}}} var got quotaList - resp, err := m.HandleManagement(context.Background(), pluginapi.ManagementRequest{Method: http.MethodPost, Path: "/v0/management/plugins/" + pluginName + "/quota", Body: []byte(`{}`)}) + resp, err := m.HandleManagement(context.Background(), pluginapi.ManagementRequest{Method: http.MethodPost, Path: "/v0/management/plugins/" + pluginName + "/quota-usage", Body: []byte(`{}`)}) if err != nil { t.Fatal(err) } @@ -73,7 +73,7 @@ func TestQuotaRefresh(t *testing.T) { m := NewManager(NewHostBridge(f.call)) m.cfg = config.Config{BaseURL: "https://quota.test/v1/", RequestTimeout: config.DefaultRequestTimeout, APIKeys: []config.APIKey{{Value: key}}} id, _ := quotaIdentity(key) - resp, err := m.HandleManagement(context.Background(), pluginapi.ManagementRequest{Method: http.MethodPost, Path: "/v0/management/plugins/" + pluginName + "/quota", Body: []byte(`{"key_id":"` + id + `"}`)}) + resp, err := m.HandleManagement(context.Background(), pluginapi.ManagementRequest{Method: http.MethodPost, Path: "/v0/management/plugins/" + pluginName + "/quota-usage", Body: []byte(`{"key_id":"` + id + `"}`)}) if err != nil { t.Fatal(err) } @@ -92,7 +92,7 @@ func TestQuotaUnknownKeyAndResource(t *testing.T) { m := NewManager(NewHostBridge(f.call)) m.cfg = config.Config{APIKeys: []config.APIKey{{Value: key}}} unknown := "opencode-go-key-unknown" - resp, err := m.HandleManagement(context.Background(), pluginapi.ManagementRequest{Method: http.MethodPost, Path: "/v0/management/plugins/" + pluginName + "/quota", Body: []byte(`{"key_id":"` + unknown + `"}`)}) + resp, err := m.HandleManagement(context.Background(), pluginapi.ManagementRequest{Method: http.MethodPost, Path: "/v0/management/plugins/" + pluginName + "/quota-usage", Body: []byte(`{"key_id":"` + unknown + `"}`)}) if err != nil || resp.StatusCode != http.StatusNotFound || strings.Contains(string(resp.Body), key) || strings.Contains(string(resp.Body), unknown) || len(f.callsOf(pluginabi.MethodHostHTTPDo)) != 0 { t.Fatalf("unknown response = %+v err=%v calls=%v", resp, err, f.callsOf(pluginabi.MethodHostHTTPDo)) } @@ -132,7 +132,7 @@ func TestQuotaErrorsAreRedacted(t *testing.T) { f := &fakeCaller{responder: responder} m := NewManager(NewHostBridge(f.call)) m.cfg = config.Config{BaseURL: "https://quota.test/v1", RequestTimeout: config.DefaultRequestTimeout, APIKeys: []config.APIKey{{Value: key}}} - resp, err := m.HandleManagement(context.Background(), pluginapi.ManagementRequest{Method: http.MethodPost, Path: "/v0/management/plugins/" + pluginName + "/quota", Body: []byte(`{"key_id":"` + id + `"}`)}) + resp, err := m.HandleManagement(context.Background(), pluginapi.ManagementRequest{Method: http.MethodPost, Path: "/v0/management/plugins/" + pluginName + "/quota-usage", Body: []byte(`{"key_id":"` + id + `"}`)}) if err != nil || resp.StatusCode != http.StatusBadGateway || strings.Contains(string(resp.Body), key) || strings.Contains(string(resp.Body), "upstream body") { t.Fatalf("response = %+v err=%v", resp, err) } diff --git a/resources/quota_page.html b/resources/quota_page.html index d565d47..24256f9 100644 --- a/resources/quota_page.html +++ b/resources/quota_page.html @@ -105,7 +105,7 @@