diff --git a/.gitattributes b/.gitattributes index 7a9c93e..b49fafa 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ patch/*.patch whitespace=-space-before-tab +*.sh text eol=lf diff --git a/internal/access/types.go b/internal/access/types.go index 77f253d..5c3a978 100644 --- a/internal/access/types.go +++ b/internal/access/types.go @@ -30,6 +30,7 @@ type ManagedKey struct { RouteMode string `json:"route_mode"` UpstreamAccountID string `json:"upstream_account_id,omitempty"` AllModels bool `json:"all_models"` + ShowInStats bool `json:"show_in_stats"` Models []string `json:"models"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` diff --git a/internal/plugin/access_test.go b/internal/plugin/access_test.go index 02e0158..71982be 100644 --- a/internal/plugin/access_test.go +++ b/internal/plugin/access_test.go @@ -197,6 +197,36 @@ func TestCreateKeyRejectsShortSecret(t *testing.T) { } } +func TestManagedKeyStatsVisibilityCanBeConfigured(t *testing.T) { + app := NewApp() + if _, err := app.HandleMethod(MethodPluginRegister, lifecycleRequest(t, SchemaVersion, testConfig(t, "enabled: true\n"))); err != nil { + t.Fatal(err) + } + response := managementCallBody(t, app, http.MethodPost, managementBase+routeKeys, []byte(`{"name":"hidden","secret":"hidden-000000","show_in_stats":false}`)) + if response.StatusCode != http.StatusCreated { + t.Fatalf("create status=%d body=%s", response.StatusCode, response.Body) + } + var created managedaccess.ManagedKey + if err := json.Unmarshal(response.Body, &created); err != nil { + t.Fatal(err) + } + if created.ShowInStats { + t.Fatalf("created key unexpectedly visible in stats: %+v", created) + } + summary := managementCall(t, app, http.MethodGet, managementBase+routeUsageSummary) + if strings.Contains(string(summary.Body), `"key_alias":"hidden"`) { + t.Fatalf("hidden key remains in dashboard: %s", summary.Body) + } + response = managementCallBody(t, app, http.MethodPatch, managementBase+routeKeys, []byte(`{"id":"`+created.ID+`","show_in_stats":true}`)) + if response.StatusCode != http.StatusOK { + t.Fatalf("update status=%d body=%s", response.StatusCode, response.Body) + } + summary = managementCall(t, app, http.MethodGet, managementBase+routeUsageSummary) + if !strings.Contains(string(summary.Body), `"key_alias":"hidden"`) { + t.Fatalf("visible key missing from dashboard: %s", summary.Body) + } +} + func TestManagedSecretValidation(t *testing.T) { for secret, want := range map[string]bool{ "12345": false, "000000": true, "alice-000000": true, "with space": false, strings.Repeat("x", 257): false, diff --git a/internal/plugin/key_management.go b/internal/plugin/key_management.go index 16782c5..90ecd9c 100644 --- a/internal/plugin/key_management.go +++ b/internal/plugin/key_management.go @@ -24,6 +24,7 @@ type createManagedKeyRequest struct { RouteMode string `json:"route_mode,omitempty"` UpstreamAccountID string `json:"upstream_account_id,omitempty"` AllModels *bool `json:"all_models,omitempty"` + ShowInStats *bool `json:"show_in_stats,omitempty"` Models *[]string `json:"models,omitempty"` Billing *billingSettingsRequest `json:"billing,omitempty"` } @@ -35,6 +36,7 @@ type updateManagedKeyRequest struct { RouteMode *string `json:"route_mode,omitempty"` UpstreamAccountID *string `json:"upstream_account_id,omitempty"` AllModels *bool `json:"all_models,omitempty"` + ShowInStats *bool `json:"show_in_stats,omitempty"` Models *[]string `json:"models,omitempty"` Billing *billingSettingsRequest `json:"billing,omitempty"` } @@ -133,7 +135,7 @@ func (a *App) createManagedKey(body []byte) ManagementResponse { } key := managedaccess.ManagedKey{ ID: keyID, Name: name, Secret: secret, - Status: managedaccess.StatusActive, RouteMode: managedaccess.RouteAuto, AllModels: true, + Status: managedaccess.StatusActive, RouteMode: managedaccess.RouteAuto, AllModels: true, ShowInStats: true, } if template, err := store.ManagedKeyByID(context.Background(), "key_default"); err == nil { key.RouteMode = template.RouteMode @@ -150,6 +152,9 @@ func (a *App) createManagedKey(body []byte) ManagementResponse { if req.AllModels != nil { key.AllModels = *req.AllModels } + if req.ShowInStats != nil { + key.ShowInStats = *req.ShowInStats + } if req.Models != nil { key.Models = normalizeModels(*req.Models) } @@ -211,6 +216,9 @@ func (a *App) updateManagedKey(body []byte) ManagementResponse { if req.AllModels != nil { key.AllModels = *req.AllModels } + if req.ShowInStats != nil { + key.ShowInStats = *req.ShowInStats + } if req.Models != nil { key.Models = normalizeModels(*req.Models) } diff --git a/internal/plugin/management_test.go b/internal/plugin/management_test.go index 9712851..026665c 100644 --- a/internal/plugin/management_test.go +++ b/internal/plugin/management_test.go @@ -665,7 +665,7 @@ func TestUsageResourceServesFeatureModules(t *testing.T) { if pageResponse.StatusCode != http.StatusOK || !strings.Contains(page, `data-view="logs">日志`) { t.Fatalf("unexpected UI response: status=%d", pageResponse.StatusCode) } - for _, feature := range []string{`
`, `