From a02547aefb5712d634356c52453925f1c9cda163 Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 7 Oct 2026 15:43:35 +0800 Subject: [PATCH] refactor: retire release acceptance API key leases --- .../scripts/test_model_context_capacity.sh | 4 +- .github/workflows/downstream-verify.yml | 7 +- backend/cmd/server/main.go | 55 ------- backend/cmd/server/main_test.go | 54 ------- backend/ent/apikey.go | 27 +--- backend/ent/apikey/apikey.go | 22 --- backend/ent/apikey/where.go | 150 ----------------- backend/ent/apikey_create.go | 153 ------------------ backend/ent/apikey_update.go | 106 ------------ backend/ent/migrate/schema.go | 26 +-- backend/ent/mutation.go | 129 +-------------- backend/ent/runtime/runtime.go | 26 +-- backend/ent/schema/api_key.go | 12 -- backend/internal/handler/dto/mappers.go | 4 - backend/internal/handler/dto/types.go | 5 - backend/internal/repository/api_key_repo.go | 64 ++------ .../api_key_repo_integration_test.go | 60 ------- .../service/admin_service_apikey_test.go | 27 ---- backend/internal/service/admin_user.go | 13 +- backend/internal/service/api_key.go | 5 - .../service/openai_codex_context_contract.go | 135 ---------------- .../openai_codex_context_contract_test.go | 68 -------- .../274_purge_release_acceptance_api_keys.sql | 73 +++++++++ ...acceptance_api_key_lease_migration_test.go | 25 --- ...e_retirement_migration_integration_test.go | 138 ++++++++++++++++ .../admin/user/UserApiKeysModal.vue | 14 +- .../src/i18n/locales/en/admin/overview.ts | 2 - .../src/i18n/locales/zh/admin/overview.ts | 2 - 28 files changed, 252 insertions(+), 1154 deletions(-) delete mode 100644 backend/cmd/server/main_test.go delete mode 100644 backend/internal/service/openai_codex_context_contract.go delete mode 100644 backend/internal/service/openai_codex_context_contract_test.go create mode 100644 backend/migrations/274_purge_release_acceptance_api_keys.sql delete mode 100644 backend/migrations/release_acceptance_api_key_lease_migration_test.go create mode 100644 backend/migrations/release_acceptance_retirement_migration_integration_test.go diff --git a/.github/scripts/test_model_context_capacity.sh b/.github/scripts/test_model_context_capacity.sh index 4825b053ed2a..712770c55f51 100644 --- a/.github/scripts/test_model_context_capacity.sh +++ b/.github/scripts/test_model_context_capacity.sh @@ -6,6 +6,6 @@ cd "$repo_root/backend" # Offline policy, persistence, and wire-contract fixtures only; no model calls. go test -p=1 -tags unit \ - ./internal/service ./internal/repository ./internal/handler ./internal/handler/admin ./cmd/server \ - -run 'Test.*(ModelContext|OfficialModelCatalog|ContextCapacit|GroupModelCapacity|CodexCapacityProjection|SyncUpstreamModelCatalog.*Capacity|RunCodexContextContractVerification)' \ + ./internal/service ./internal/repository ./internal/handler ./internal/handler/admin \ + -run 'Test.*(ModelContext|OfficialModelCatalog|ContextCapacit|GroupModelCapacity|CodexCapacityProjection|SyncUpstreamModelCatalog.*Capacity)' \ -count=1 diff --git a/.github/workflows/downstream-verify.yml b/.github/workflows/downstream-verify.yml index 09ab20dcb0b3..c3fedebae90f 100644 --- a/.github/workflows/downstream-verify.yml +++ b/.github/workflows/downstream-verify.yml @@ -55,7 +55,7 @@ jobs: working-directory: backend run: | go test -p=1 -tags unit ./internal/repository ./internal/service ./internal/handler ./internal/handler/admin -run 'Test.*(CodexImageGenerationBridgeOverridePrecedence|TurnStateCommits|SchedulerCache|GatewayModels|WriteOpenAIModelsList|AccountHandlerGetAvailableModels|ForwardResponsesOllamaClampPreservesContinuationPolicy|AccountAvailableModels|FetchOpenAIAccountModels|SetAnthropicAPIKeyAuthHeader|OllamaCloud.*(MaxTokens|MaxOutputTokens|Bearer)|Clamps.*OllamaBase)' -count=1 - go test -p=1 -tags unit ./migrations -run 'Test.*(Migration222|Migration223|Migration224|Migration230|Migration231|Migration232|Migration235|Migration236|GroupModelAllowlistRepairMigration|ReleaseAcceptance)' -count=1 + go test -p=1 -tags unit ./migrations -run 'Test.*(Migration222|Migration223|Migration224|Migration230|Migration231|Migration232|Migration235|Migration236|GroupModelAllowlistRepairMigration)' -count=1 - name: Request body recovery tests working-directory: backend run: go test -p=1 ./internal/pkg/httputil ./internal/handler -run 'Test(ReadRequestBodyWithPrealloc|ReadLenientJSONRequestBodyWithPrealloc|RequestBodyReadDiagnostics|ClassifyRequestContentEncoding)' -count=1 @@ -71,7 +71,10 @@ jobs: run: go test -p=1 -tags unit ./internal/handler/admin -run '^TestAccountJobBulk.*$' -count=1 - name: Account taxonomy migration integration test working-directory: backend - run: go test -p=1 -tags integration ./internal/repository -run '^Test(AccountTaxonomy.*|DeleteAccountFolder.*)Integration$|^TestAPIKeyRepoSuite$/^TestReleaseAcceptanceKeyIsAdminListedReadOnlyAndAuthenticates$' -count=1 + run: go test -p=1 -tags integration ./internal/repository -run '^Test(AccountTaxonomy.*|DeleteAccountFolder.*)Integration$' -count=1 + - name: Release acceptance retirement migration integration test + working-directory: backend + run: go test -p=1 -tags integration ./migrations -run '^TestMigration274RemovesReleaseAcceptanceKeysSafely$' -count=1 - name: Repository audit integration tests working-directory: backend run: go test -p=1 -tags integration ./internal/repository -run '^Test(PGXBatchInsertAuditAndSystemLogs|Migration236.*)$' -count=1 diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index 9caac8fe13b7..d34868803cc8 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -7,8 +7,6 @@ import ( _ "embed" "errors" "flag" - "fmt" - "io" "log" "net/http" "os" @@ -22,7 +20,6 @@ import ( "github.com/Wei-Shaw/sub2api/internal/handler" "github.com/Wei-Shaw/sub2api/internal/pkg/logger" "github.com/Wei-Shaw/sub2api/internal/server/middleware" - "github.com/Wei-Shaw/sub2api/internal/service" "github.com/Wei-Shaw/sub2api/internal/setup" "github.com/Wei-Shaw/sub2api/internal/web" @@ -62,24 +59,7 @@ func main() { // Parse command line flags setupMode := flag.Bool("setup", false, "Run setup wizard in CLI mode") showVersion := flag.Bool("version", false, "Show version information") - verifyCodexContextContract := flag.Bool( - "verify-codex-context-contract", - false, - "Verify ordinary-provider context capacity priority and protected-manifest contracts offline", - ) flag.Parse() - if *verifyCodexContextContract { - exitCode := runCodexContextContractVerification( - os.Stdout, - *setupMode, - *showVersion, - flag.Args(), - ) - if exitCode != 0 { - os.Exit(exitCode) - } - return - } if *showVersion { log.Printf("Sub2API %s (commit: %s, built: %s)\n", Version, Commit, Date) @@ -114,41 +94,6 @@ func main() { runMainServer() } -func runCodexContextContractVerification( - stdout io.Writer, - setupMode bool, - showVersion bool, - positionalArgs []string, -) int { - return runCodexContextContractVerificationWith( - stdout, - setupMode, - showVersion, - positionalArgs, - service.VerifyOfficialCodexContextContract, - ) -} - -func runCodexContextContractVerificationWith( - stdout io.Writer, - setupMode bool, - showVersion bool, - positionalArgs []string, - verify func() (string, error), -) int { - if setupMode || showVersion || len(positionalArgs) != 0 { - _, _ = fmt.Fprintln(stdout, service.OfficialCodexContextInvalidArgsLine) - return 2 - } - line, err := verify() - if err != nil { - _, _ = fmt.Fprintln(stdout, service.OfficialCodexContextContractFailureLine) - return 1 - } - _, _ = fmt.Fprintln(stdout, line) - return 0 -} - func runSetupServer() { r := gin.New() r.Use(middleware.Recovery()) diff --git a/backend/cmd/server/main_test.go b/backend/cmd/server/main_test.go deleted file mode 100644 index 2544082e28fb..000000000000 --- a/backend/cmd/server/main_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -import ( - "bytes" - "errors" - "testing" - - "github.com/Wei-Shaw/sub2api/internal/service" - "github.com/stretchr/testify/require" -) - -func TestRunCodexContextContractVerification(t *testing.T) { - var output bytes.Buffer - require.Equal(t, 0, runCodexContextContractVerification(&output, false, false, nil)) - require.Equal(t, service.OfficialCodexContextContractSuccessLine+"\n", output.String()) -} - -func TestRunCodexContextContractVerificationFailsWithFixedShape(t *testing.T) { - var output bytes.Buffer - exitCode := runCodexContextContractVerificationWith( - &output, - false, - false, - nil, - func() (string, error) { return "sensitive-detail", errors.New("sensitive-detail") }, - ) - require.Equal(t, 1, exitCode) - require.Equal(t, service.OfficialCodexContextContractFailureLine+"\n", output.String()) - require.NotContains(t, output.String(), "sensitive-detail") -} - -func TestRunCodexContextContractVerificationRejectsConflictingArguments(t *testing.T) { - tests := []struct { - name string - setup, version bool - args []string - }{ - {name: "setup", setup: true}, - {name: "version", version: true}, - {name: "positional", args: []string{"unexpected"}}, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var output bytes.Buffer - require.Equal(t, 2, runCodexContextContractVerification( - &output, - test.setup, - test.version, - test.args, - )) - require.Equal(t, service.OfficialCodexContextInvalidArgsLine+"\n", output.String()) - }) - } -} diff --git a/backend/ent/apikey.go b/backend/ent/apikey.go index c9774b9ef630..9ee660c2da69 100644 --- a/backend/ent/apikey.go +++ b/backend/ent/apikey.go @@ -36,10 +36,6 @@ type APIKey struct { GroupID *int64 `json:"group_id,omitempty"` // Status holds the value of the "status" field. Status string `json:"status,omitempty"` - // Internal lifecycle purpose; user keys remain the public default - Purpose string `json:"purpose,omitempty"` - // Opaque idempotency identifier for short-lived internal leases - LeaseID *string `json:"lease_id,omitempty"` // Last usage time of this API key LastUsedAt *time.Time `json:"last_used_at,omitempty"` // Allowed IPs/CIDRs, e.g. ["192.168.1.100", "10.0.0.0/8"] @@ -131,7 +127,7 @@ func (*APIKey) scanValues(columns []string) ([]any, error) { values[i] = new(sql.NullFloat64) case apikey.FieldID, apikey.FieldUserID, apikey.FieldGroupID: values[i] = new(sql.NullInt64) - case apikey.FieldKey, apikey.FieldName, apikey.FieldStatus, apikey.FieldPurpose, apikey.FieldLeaseID: + case apikey.FieldKey, apikey.FieldName, apikey.FieldStatus: values[i] = new(sql.NullString) case apikey.FieldCreatedAt, apikey.FieldUpdatedAt, apikey.FieldDeletedAt, apikey.FieldLastUsedAt, apikey.FieldExpiresAt, apikey.FieldWindow5hStart, apikey.FieldWindow1dStart, apikey.FieldWindow7dStart: values[i] = new(sql.NullTime) @@ -206,19 +202,6 @@ func (_m *APIKey) assignValues(columns []string, values []any) error { } else if value.Valid { _m.Status = value.String } - case apikey.FieldPurpose: - if value, ok := values[i].(*sql.NullString); !ok { - return fmt.Errorf("unexpected type %T for field purpose", values[i]) - } else if value.Valid { - _m.Purpose = value.String - } - case apikey.FieldLeaseID: - if value, ok := values[i].(*sql.NullString); !ok { - return fmt.Errorf("unexpected type %T for field lease_id", values[i]) - } else if value.Valid { - _m.LeaseID = new(string) - *_m.LeaseID = value.String - } case apikey.FieldLastUsedAt: if value, ok := values[i].(*sql.NullTime); !ok { return fmt.Errorf("unexpected type %T for field last_used_at", values[i]) @@ -397,14 +380,6 @@ func (_m *APIKey) String() string { builder.WriteString("status=") builder.WriteString(_m.Status) builder.WriteString(", ") - builder.WriteString("purpose=") - builder.WriteString(_m.Purpose) - builder.WriteString(", ") - if v := _m.LeaseID; v != nil { - builder.WriteString("lease_id=") - builder.WriteString(*v) - } - builder.WriteString(", ") if v := _m.LastUsedAt; v != nil { builder.WriteString("last_used_at=") builder.WriteString(v.Format(time.ANSIC)) diff --git a/backend/ent/apikey/apikey.go b/backend/ent/apikey/apikey.go index 5e08424c74aa..d398a027b867 100644 --- a/backend/ent/apikey/apikey.go +++ b/backend/ent/apikey/apikey.go @@ -31,10 +31,6 @@ const ( FieldGroupID = "group_id" // FieldStatus holds the string denoting the status field in the database. FieldStatus = "status" - // FieldPurpose holds the string denoting the purpose field in the database. - FieldPurpose = "purpose" - // FieldLeaseID holds the string denoting the lease_id field in the database. - FieldLeaseID = "lease_id" // FieldLastUsedAt holds the string denoting the last_used_at field in the database. FieldLastUsedAt = "last_used_at" // FieldIPWhitelist holds the string denoting the ip_whitelist field in the database. @@ -107,8 +103,6 @@ var Columns = []string{ FieldName, FieldGroupID, FieldStatus, - FieldPurpose, - FieldLeaseID, FieldLastUsedAt, FieldIPWhitelist, FieldIPBlacklist, @@ -158,12 +152,6 @@ var ( DefaultStatus string // StatusValidator is a validator for the "status" field. It is called by the builders before save. StatusValidator func(string) error - // DefaultPurpose holds the default value on creation for the "purpose" field. - DefaultPurpose string - // PurposeValidator is a validator for the "purpose" field. It is called by the builders before save. - PurposeValidator func(string) error - // LeaseIDValidator is a validator for the "lease_id" field. It is called by the builders before save. - LeaseIDValidator func(string) error // DefaultQuota holds the default value on creation for the "quota" field. DefaultQuota float64 // DefaultQuotaUsed holds the default value on creation for the "quota_used" field. @@ -230,16 +218,6 @@ func ByStatus(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldStatus, opts...).ToFunc() } -// ByPurpose orders the results by the purpose field. -func ByPurpose(opts ...sql.OrderTermOption) OrderOption { - return sql.OrderByField(FieldPurpose, opts...).ToFunc() -} - -// ByLeaseID orders the results by the lease_id field. -func ByLeaseID(opts ...sql.OrderTermOption) OrderOption { - return sql.OrderByField(FieldLeaseID, opts...).ToFunc() -} - // ByLastUsedAt orders the results by the last_used_at field. func ByLastUsedAt(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldLastUsedAt, opts...).ToFunc() diff --git a/backend/ent/apikey/where.go b/backend/ent/apikey/where.go index fcafdf837eaf..edd2652baaed 100644 --- a/backend/ent/apikey/where.go +++ b/backend/ent/apikey/where.go @@ -95,16 +95,6 @@ func Status(v string) predicate.APIKey { return predicate.APIKey(sql.FieldEQ(FieldStatus, v)) } -// Purpose applies equality check predicate on the "purpose" field. It's identical to PurposeEQ. -func Purpose(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldEQ(FieldPurpose, v)) -} - -// LeaseID applies equality check predicate on the "lease_id" field. It's identical to LeaseIDEQ. -func LeaseID(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldEQ(FieldLeaseID, v)) -} - // LastUsedAt applies equality check predicate on the "last_used_at" field. It's identical to LastUsedAtEQ. func LastUsedAt(v time.Time) predicate.APIKey { return predicate.APIKey(sql.FieldEQ(FieldLastUsedAt, v)) @@ -545,146 +535,6 @@ func StatusContainsFold(v string) predicate.APIKey { return predicate.APIKey(sql.FieldContainsFold(FieldStatus, v)) } -// PurposeEQ applies the EQ predicate on the "purpose" field. -func PurposeEQ(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldEQ(FieldPurpose, v)) -} - -// PurposeNEQ applies the NEQ predicate on the "purpose" field. -func PurposeNEQ(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldNEQ(FieldPurpose, v)) -} - -// PurposeIn applies the In predicate on the "purpose" field. -func PurposeIn(vs ...string) predicate.APIKey { - return predicate.APIKey(sql.FieldIn(FieldPurpose, vs...)) -} - -// PurposeNotIn applies the NotIn predicate on the "purpose" field. -func PurposeNotIn(vs ...string) predicate.APIKey { - return predicate.APIKey(sql.FieldNotIn(FieldPurpose, vs...)) -} - -// PurposeGT applies the GT predicate on the "purpose" field. -func PurposeGT(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldGT(FieldPurpose, v)) -} - -// PurposeGTE applies the GTE predicate on the "purpose" field. -func PurposeGTE(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldGTE(FieldPurpose, v)) -} - -// PurposeLT applies the LT predicate on the "purpose" field. -func PurposeLT(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldLT(FieldPurpose, v)) -} - -// PurposeLTE applies the LTE predicate on the "purpose" field. -func PurposeLTE(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldLTE(FieldPurpose, v)) -} - -// PurposeContains applies the Contains predicate on the "purpose" field. -func PurposeContains(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldContains(FieldPurpose, v)) -} - -// PurposeHasPrefix applies the HasPrefix predicate on the "purpose" field. -func PurposeHasPrefix(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldHasPrefix(FieldPurpose, v)) -} - -// PurposeHasSuffix applies the HasSuffix predicate on the "purpose" field. -func PurposeHasSuffix(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldHasSuffix(FieldPurpose, v)) -} - -// PurposeEqualFold applies the EqualFold predicate on the "purpose" field. -func PurposeEqualFold(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldEqualFold(FieldPurpose, v)) -} - -// PurposeContainsFold applies the ContainsFold predicate on the "purpose" field. -func PurposeContainsFold(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldContainsFold(FieldPurpose, v)) -} - -// LeaseIDEQ applies the EQ predicate on the "lease_id" field. -func LeaseIDEQ(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldEQ(FieldLeaseID, v)) -} - -// LeaseIDNEQ applies the NEQ predicate on the "lease_id" field. -func LeaseIDNEQ(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldNEQ(FieldLeaseID, v)) -} - -// LeaseIDIn applies the In predicate on the "lease_id" field. -func LeaseIDIn(vs ...string) predicate.APIKey { - return predicate.APIKey(sql.FieldIn(FieldLeaseID, vs...)) -} - -// LeaseIDNotIn applies the NotIn predicate on the "lease_id" field. -func LeaseIDNotIn(vs ...string) predicate.APIKey { - return predicate.APIKey(sql.FieldNotIn(FieldLeaseID, vs...)) -} - -// LeaseIDGT applies the GT predicate on the "lease_id" field. -func LeaseIDGT(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldGT(FieldLeaseID, v)) -} - -// LeaseIDGTE applies the GTE predicate on the "lease_id" field. -func LeaseIDGTE(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldGTE(FieldLeaseID, v)) -} - -// LeaseIDLT applies the LT predicate on the "lease_id" field. -func LeaseIDLT(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldLT(FieldLeaseID, v)) -} - -// LeaseIDLTE applies the LTE predicate on the "lease_id" field. -func LeaseIDLTE(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldLTE(FieldLeaseID, v)) -} - -// LeaseIDContains applies the Contains predicate on the "lease_id" field. -func LeaseIDContains(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldContains(FieldLeaseID, v)) -} - -// LeaseIDHasPrefix applies the HasPrefix predicate on the "lease_id" field. -func LeaseIDHasPrefix(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldHasPrefix(FieldLeaseID, v)) -} - -// LeaseIDHasSuffix applies the HasSuffix predicate on the "lease_id" field. -func LeaseIDHasSuffix(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldHasSuffix(FieldLeaseID, v)) -} - -// LeaseIDIsNil applies the IsNil predicate on the "lease_id" field. -func LeaseIDIsNil() predicate.APIKey { - return predicate.APIKey(sql.FieldIsNull(FieldLeaseID)) -} - -// LeaseIDNotNil applies the NotNil predicate on the "lease_id" field. -func LeaseIDNotNil() predicate.APIKey { - return predicate.APIKey(sql.FieldNotNull(FieldLeaseID)) -} - -// LeaseIDEqualFold applies the EqualFold predicate on the "lease_id" field. -func LeaseIDEqualFold(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldEqualFold(FieldLeaseID, v)) -} - -// LeaseIDContainsFold applies the ContainsFold predicate on the "lease_id" field. -func LeaseIDContainsFold(v string) predicate.APIKey { - return predicate.APIKey(sql.FieldContainsFold(FieldLeaseID, v)) -} - // LastUsedAtEQ applies the EQ predicate on the "last_used_at" field. func LastUsedAtEQ(v time.Time) predicate.APIKey { return predicate.APIKey(sql.FieldEQ(FieldLastUsedAt, v)) diff --git a/backend/ent/apikey_create.go b/backend/ent/apikey_create.go index 44015d0f729e..4ec8aeaae4e3 100644 --- a/backend/ent/apikey_create.go +++ b/backend/ent/apikey_create.go @@ -113,34 +113,6 @@ func (_c *APIKeyCreate) SetNillableStatus(v *string) *APIKeyCreate { return _c } -// SetPurpose sets the "purpose" field. -func (_c *APIKeyCreate) SetPurpose(v string) *APIKeyCreate { - _c.mutation.SetPurpose(v) - return _c -} - -// SetNillablePurpose sets the "purpose" field if the given value is not nil. -func (_c *APIKeyCreate) SetNillablePurpose(v *string) *APIKeyCreate { - if v != nil { - _c.SetPurpose(*v) - } - return _c -} - -// SetLeaseID sets the "lease_id" field. -func (_c *APIKeyCreate) SetLeaseID(v string) *APIKeyCreate { - _c.mutation.SetLeaseID(v) - return _c -} - -// SetNillableLeaseID sets the "lease_id" field if the given value is not nil. -func (_c *APIKeyCreate) SetNillableLeaseID(v *string) *APIKeyCreate { - if v != nil { - _c.SetLeaseID(*v) - } - return _c -} - // SetLastUsedAt sets the "last_used_at" field. func (_c *APIKeyCreate) SetLastUsedAt(v time.Time) *APIKeyCreate { _c.mutation.SetLastUsedAt(v) @@ -415,10 +387,6 @@ func (_c *APIKeyCreate) defaults() error { v := apikey.DefaultStatus _c.mutation.SetStatus(v) } - if _, ok := _c.mutation.Purpose(); !ok { - v := apikey.DefaultPurpose - _c.mutation.SetPurpose(v) - } if _, ok := _c.mutation.Quota(); !ok { v := apikey.DefaultQuota _c.mutation.SetQuota(v) @@ -489,19 +457,6 @@ func (_c *APIKeyCreate) check() error { return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "APIKey.status": %w`, err)} } } - if _, ok := _c.mutation.Purpose(); !ok { - return &ValidationError{Name: "purpose", err: errors.New(`ent: missing required field "APIKey.purpose"`)} - } - if v, ok := _c.mutation.Purpose(); ok { - if err := apikey.PurposeValidator(v); err != nil { - return &ValidationError{Name: "purpose", err: fmt.Errorf(`ent: validator failed for field "APIKey.purpose": %w`, err)} - } - } - if v, ok := _c.mutation.LeaseID(); ok { - if err := apikey.LeaseIDValidator(v); err != nil { - return &ValidationError{Name: "lease_id", err: fmt.Errorf(`ent: validator failed for field "APIKey.lease_id": %w`, err)} - } - } if _, ok := _c.mutation.Quota(); !ok { return &ValidationError{Name: "quota", err: errors.New(`ent: missing required field "APIKey.quota"`)} } @@ -580,14 +535,6 @@ func (_c *APIKeyCreate) createSpec() (*APIKey, *sqlgraph.CreateSpec) { _spec.SetField(apikey.FieldStatus, field.TypeString, value) _node.Status = value } - if value, ok := _c.mutation.Purpose(); ok { - _spec.SetField(apikey.FieldPurpose, field.TypeString, value) - _node.Purpose = value - } - if value, ok := _c.mutation.LeaseID(); ok { - _spec.SetField(apikey.FieldLeaseID, field.TypeString, value) - _node.LeaseID = &value - } if value, ok := _c.mutation.LastUsedAt(); ok { _spec.SetField(apikey.FieldLastUsedAt, field.TypeTime, value) _node.LastUsedAt = &value @@ -846,36 +793,6 @@ func (u *APIKeyUpsert) UpdateStatus() *APIKeyUpsert { return u } -// SetPurpose sets the "purpose" field. -func (u *APIKeyUpsert) SetPurpose(v string) *APIKeyUpsert { - u.Set(apikey.FieldPurpose, v) - return u -} - -// UpdatePurpose sets the "purpose" field to the value that was provided on create. -func (u *APIKeyUpsert) UpdatePurpose() *APIKeyUpsert { - u.SetExcluded(apikey.FieldPurpose) - return u -} - -// SetLeaseID sets the "lease_id" field. -func (u *APIKeyUpsert) SetLeaseID(v string) *APIKeyUpsert { - u.Set(apikey.FieldLeaseID, v) - return u -} - -// UpdateLeaseID sets the "lease_id" field to the value that was provided on create. -func (u *APIKeyUpsert) UpdateLeaseID() *APIKeyUpsert { - u.SetExcluded(apikey.FieldLeaseID) - return u -} - -// ClearLeaseID clears the value of the "lease_id" field. -func (u *APIKeyUpsert) ClearLeaseID() *APIKeyUpsert { - u.SetNull(apikey.FieldLeaseID) - return u -} - // SetLastUsedAt sets the "last_used_at" field. func (u *APIKeyUpsert) SetLastUsedAt(v time.Time) *APIKeyUpsert { u.Set(apikey.FieldLastUsedAt, v) @@ -1303,41 +1220,6 @@ func (u *APIKeyUpsertOne) UpdateStatus() *APIKeyUpsertOne { }) } -// SetPurpose sets the "purpose" field. -func (u *APIKeyUpsertOne) SetPurpose(v string) *APIKeyUpsertOne { - return u.Update(func(s *APIKeyUpsert) { - s.SetPurpose(v) - }) -} - -// UpdatePurpose sets the "purpose" field to the value that was provided on create. -func (u *APIKeyUpsertOne) UpdatePurpose() *APIKeyUpsertOne { - return u.Update(func(s *APIKeyUpsert) { - s.UpdatePurpose() - }) -} - -// SetLeaseID sets the "lease_id" field. -func (u *APIKeyUpsertOne) SetLeaseID(v string) *APIKeyUpsertOne { - return u.Update(func(s *APIKeyUpsert) { - s.SetLeaseID(v) - }) -} - -// UpdateLeaseID sets the "lease_id" field to the value that was provided on create. -func (u *APIKeyUpsertOne) UpdateLeaseID() *APIKeyUpsertOne { - return u.Update(func(s *APIKeyUpsert) { - s.UpdateLeaseID() - }) -} - -// ClearLeaseID clears the value of the "lease_id" field. -func (u *APIKeyUpsertOne) ClearLeaseID() *APIKeyUpsertOne { - return u.Update(func(s *APIKeyUpsert) { - s.ClearLeaseID() - }) -} - // SetLastUsedAt sets the "last_used_at" field. func (u *APIKeyUpsertOne) SetLastUsedAt(v time.Time) *APIKeyUpsertOne { return u.Update(func(s *APIKeyUpsert) { @@ -1976,41 +1858,6 @@ func (u *APIKeyUpsertBulk) UpdateStatus() *APIKeyUpsertBulk { }) } -// SetPurpose sets the "purpose" field. -func (u *APIKeyUpsertBulk) SetPurpose(v string) *APIKeyUpsertBulk { - return u.Update(func(s *APIKeyUpsert) { - s.SetPurpose(v) - }) -} - -// UpdatePurpose sets the "purpose" field to the value that was provided on create. -func (u *APIKeyUpsertBulk) UpdatePurpose() *APIKeyUpsertBulk { - return u.Update(func(s *APIKeyUpsert) { - s.UpdatePurpose() - }) -} - -// SetLeaseID sets the "lease_id" field. -func (u *APIKeyUpsertBulk) SetLeaseID(v string) *APIKeyUpsertBulk { - return u.Update(func(s *APIKeyUpsert) { - s.SetLeaseID(v) - }) -} - -// UpdateLeaseID sets the "lease_id" field to the value that was provided on create. -func (u *APIKeyUpsertBulk) UpdateLeaseID() *APIKeyUpsertBulk { - return u.Update(func(s *APIKeyUpsert) { - s.UpdateLeaseID() - }) -} - -// ClearLeaseID clears the value of the "lease_id" field. -func (u *APIKeyUpsertBulk) ClearLeaseID() *APIKeyUpsertBulk { - return u.Update(func(s *APIKeyUpsert) { - s.ClearLeaseID() - }) -} - // SetLastUsedAt sets the "last_used_at" field. func (u *APIKeyUpsertBulk) SetLastUsedAt(v time.Time) *APIKeyUpsertBulk { return u.Update(func(s *APIKeyUpsert) { diff --git a/backend/ent/apikey_update.go b/backend/ent/apikey_update.go index 6f586fc72dc3..db341e4c9d53 100644 --- a/backend/ent/apikey_update.go +++ b/backend/ent/apikey_update.go @@ -134,40 +134,6 @@ func (_u *APIKeyUpdate) SetNillableStatus(v *string) *APIKeyUpdate { return _u } -// SetPurpose sets the "purpose" field. -func (_u *APIKeyUpdate) SetPurpose(v string) *APIKeyUpdate { - _u.mutation.SetPurpose(v) - return _u -} - -// SetNillablePurpose sets the "purpose" field if the given value is not nil. -func (_u *APIKeyUpdate) SetNillablePurpose(v *string) *APIKeyUpdate { - if v != nil { - _u.SetPurpose(*v) - } - return _u -} - -// SetLeaseID sets the "lease_id" field. -func (_u *APIKeyUpdate) SetLeaseID(v string) *APIKeyUpdate { - _u.mutation.SetLeaseID(v) - return _u -} - -// SetNillableLeaseID sets the "lease_id" field if the given value is not nil. -func (_u *APIKeyUpdate) SetNillableLeaseID(v *string) *APIKeyUpdate { - if v != nil { - _u.SetLeaseID(*v) - } - return _u -} - -// ClearLeaseID clears the value of the "lease_id" field. -func (_u *APIKeyUpdate) ClearLeaseID() *APIKeyUpdate { - _u.mutation.ClearLeaseID() - return _u -} - // SetLastUsedAt sets the "last_used_at" field. func (_u *APIKeyUpdate) SetLastUsedAt(v time.Time) *APIKeyUpdate { _u.mutation.SetLastUsedAt(v) @@ -594,16 +560,6 @@ func (_u *APIKeyUpdate) check() error { return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "APIKey.status": %w`, err)} } } - if v, ok := _u.mutation.Purpose(); ok { - if err := apikey.PurposeValidator(v); err != nil { - return &ValidationError{Name: "purpose", err: fmt.Errorf(`ent: validator failed for field "APIKey.purpose": %w`, err)} - } - } - if v, ok := _u.mutation.LeaseID(); ok { - if err := apikey.LeaseIDValidator(v); err != nil { - return &ValidationError{Name: "lease_id", err: fmt.Errorf(`ent: validator failed for field "APIKey.lease_id": %w`, err)} - } - } if _u.mutation.UserCleared() && len(_u.mutation.UserIDs()) > 0 { return errors.New(`ent: clearing a required unique edge "APIKey.user"`) } @@ -640,15 +596,6 @@ func (_u *APIKeyUpdate) sqlSave(ctx context.Context) (_node int, err error) { if value, ok := _u.mutation.Status(); ok { _spec.SetField(apikey.FieldStatus, field.TypeString, value) } - if value, ok := _u.mutation.Purpose(); ok { - _spec.SetField(apikey.FieldPurpose, field.TypeString, value) - } - if value, ok := _u.mutation.LeaseID(); ok { - _spec.SetField(apikey.FieldLeaseID, field.TypeString, value) - } - if _u.mutation.LeaseIDCleared() { - _spec.ClearField(apikey.FieldLeaseID, field.TypeString) - } if value, ok := _u.mutation.LastUsedAt(); ok { _spec.SetField(apikey.FieldLastUsedAt, field.TypeTime, value) } @@ -974,40 +921,6 @@ func (_u *APIKeyUpdateOne) SetNillableStatus(v *string) *APIKeyUpdateOne { return _u } -// SetPurpose sets the "purpose" field. -func (_u *APIKeyUpdateOne) SetPurpose(v string) *APIKeyUpdateOne { - _u.mutation.SetPurpose(v) - return _u -} - -// SetNillablePurpose sets the "purpose" field if the given value is not nil. -func (_u *APIKeyUpdateOne) SetNillablePurpose(v *string) *APIKeyUpdateOne { - if v != nil { - _u.SetPurpose(*v) - } - return _u -} - -// SetLeaseID sets the "lease_id" field. -func (_u *APIKeyUpdateOne) SetLeaseID(v string) *APIKeyUpdateOne { - _u.mutation.SetLeaseID(v) - return _u -} - -// SetNillableLeaseID sets the "lease_id" field if the given value is not nil. -func (_u *APIKeyUpdateOne) SetNillableLeaseID(v *string) *APIKeyUpdateOne { - if v != nil { - _u.SetLeaseID(*v) - } - return _u -} - -// ClearLeaseID clears the value of the "lease_id" field. -func (_u *APIKeyUpdateOne) ClearLeaseID() *APIKeyUpdateOne { - _u.mutation.ClearLeaseID() - return _u -} - // SetLastUsedAt sets the "last_used_at" field. func (_u *APIKeyUpdateOne) SetLastUsedAt(v time.Time) *APIKeyUpdateOne { _u.mutation.SetLastUsedAt(v) @@ -1447,16 +1360,6 @@ func (_u *APIKeyUpdateOne) check() error { return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "APIKey.status": %w`, err)} } } - if v, ok := _u.mutation.Purpose(); ok { - if err := apikey.PurposeValidator(v); err != nil { - return &ValidationError{Name: "purpose", err: fmt.Errorf(`ent: validator failed for field "APIKey.purpose": %w`, err)} - } - } - if v, ok := _u.mutation.LeaseID(); ok { - if err := apikey.LeaseIDValidator(v); err != nil { - return &ValidationError{Name: "lease_id", err: fmt.Errorf(`ent: validator failed for field "APIKey.lease_id": %w`, err)} - } - } if _u.mutation.UserCleared() && len(_u.mutation.UserIDs()) > 0 { return errors.New(`ent: clearing a required unique edge "APIKey.user"`) } @@ -1510,15 +1413,6 @@ func (_u *APIKeyUpdateOne) sqlSave(ctx context.Context) (_node *APIKey, err erro if value, ok := _u.mutation.Status(); ok { _spec.SetField(apikey.FieldStatus, field.TypeString, value) } - if value, ok := _u.mutation.Purpose(); ok { - _spec.SetField(apikey.FieldPurpose, field.TypeString, value) - } - if value, ok := _u.mutation.LeaseID(); ok { - _spec.SetField(apikey.FieldLeaseID, field.TypeString, value) - } - if _u.mutation.LeaseIDCleared() { - _spec.ClearField(apikey.FieldLeaseID, field.TypeString) - } if value, ok := _u.mutation.LastUsedAt(); ok { _spec.SetField(apikey.FieldLastUsedAt, field.TypeTime, value) } diff --git a/backend/ent/migrate/schema.go b/backend/ent/migrate/schema.go index fcf0d4fccb20..ab102c5c72fd 100644 --- a/backend/ent/migrate/schema.go +++ b/backend/ent/migrate/schema.go @@ -18,8 +18,6 @@ var ( {Name: "key", Type: field.TypeString, Unique: true, Size: 128}, {Name: "name", Type: field.TypeString, Size: 100}, {Name: "status", Type: field.TypeString, Size: 20, Default: "active"}, - {Name: "purpose", Type: field.TypeString, Size: 32, Default: "user"}, - {Name: "lease_id", Type: field.TypeString, Unique: true, Nullable: true, Size: 64}, {Name: "last_used_at", Type: field.TypeTime, Nullable: true}, {Name: "ip_whitelist", Type: field.TypeJSON, Nullable: true}, {Name: "ip_blacklist", Type: field.TypeJSON, Nullable: true}, @@ -46,13 +44,13 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "api_keys_groups_api_keys", - Columns: []*schema.Column{APIKeysColumns[24]}, + Columns: []*schema.Column{APIKeysColumns[22]}, RefColumns: []*schema.Column{GroupsColumns[0]}, OnDelete: schema.SetNull, }, { Symbol: "api_keys_users_api_keys", - Columns: []*schema.Column{APIKeysColumns[25]}, + Columns: []*schema.Column{APIKeysColumns[23]}, RefColumns: []*schema.Column{UsersColumns[0]}, OnDelete: schema.NoAction, }, @@ -61,28 +59,18 @@ var ( { Name: "apikey_user_id", Unique: false, - Columns: []*schema.Column{APIKeysColumns[25]}, + Columns: []*schema.Column{APIKeysColumns[23]}, }, { Name: "apikey_group_id", Unique: false, - Columns: []*schema.Column{APIKeysColumns[24]}, + Columns: []*schema.Column{APIKeysColumns[22]}, }, { Name: "apikey_status", Unique: false, Columns: []*schema.Column{APIKeysColumns[6]}, }, - { - Name: "apikey_purpose", - Unique: false, - Columns: []*schema.Column{APIKeysColumns[7]}, - }, - { - Name: "apikey_purpose_expires_at", - Unique: false, - Columns: []*schema.Column{APIKeysColumns[7], APIKeysColumns[14]}, - }, { Name: "apikey_deleted_at", Unique: false, @@ -91,17 +79,17 @@ var ( { Name: "apikey_last_used_at", Unique: false, - Columns: []*schema.Column{APIKeysColumns[9]}, + Columns: []*schema.Column{APIKeysColumns[7]}, }, { Name: "apikey_quota_quota_used", Unique: false, - Columns: []*schema.Column{APIKeysColumns[12], APIKeysColumns[13]}, + Columns: []*schema.Column{APIKeysColumns[10], APIKeysColumns[11]}, }, { Name: "apikey_expires_at", Unique: false, - Columns: []*schema.Column{APIKeysColumns[14]}, + Columns: []*schema.Column{APIKeysColumns[12]}, }, }, } diff --git a/backend/ent/mutation.go b/backend/ent/mutation.go index 54f37900af36..9283f899bb61 100644 --- a/backend/ent/mutation.go +++ b/backend/ent/mutation.go @@ -123,8 +123,6 @@ type APIKeyMutation struct { key *string name *string status *string - purpose *string - lease_id *string last_used_at *time.Time ip_whitelist *[]string appendip_whitelist []string @@ -575,91 +573,6 @@ func (m *APIKeyMutation) ResetStatus() { m.status = nil } -// SetPurpose sets the "purpose" field. -func (m *APIKeyMutation) SetPurpose(s string) { - m.purpose = &s -} - -// Purpose returns the value of the "purpose" field in the mutation. -func (m *APIKeyMutation) Purpose() (r string, exists bool) { - v := m.purpose - if v == nil { - return - } - return *v, true -} - -// OldPurpose returns the old "purpose" field's value of the APIKey entity. -// If the APIKey object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *APIKeyMutation) OldPurpose(ctx context.Context) (v string, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldPurpose is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldPurpose requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldPurpose: %w", err) - } - return oldValue.Purpose, nil -} - -// ResetPurpose resets all changes to the "purpose" field. -func (m *APIKeyMutation) ResetPurpose() { - m.purpose = nil -} - -// SetLeaseID sets the "lease_id" field. -func (m *APIKeyMutation) SetLeaseID(s string) { - m.lease_id = &s -} - -// LeaseID returns the value of the "lease_id" field in the mutation. -func (m *APIKeyMutation) LeaseID() (r string, exists bool) { - v := m.lease_id - if v == nil { - return - } - return *v, true -} - -// OldLeaseID returns the old "lease_id" field's value of the APIKey entity. -// If the APIKey object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *APIKeyMutation) OldLeaseID(ctx context.Context) (v *string, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldLeaseID is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldLeaseID requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldLeaseID: %w", err) - } - return oldValue.LeaseID, nil -} - -// ClearLeaseID clears the value of the "lease_id" field. -func (m *APIKeyMutation) ClearLeaseID() { - m.lease_id = nil - m.clearedFields[apikey.FieldLeaseID] = struct{}{} -} - -// LeaseIDCleared returns if the "lease_id" field was cleared in this mutation. -func (m *APIKeyMutation) LeaseIDCleared() bool { - _, ok := m.clearedFields[apikey.FieldLeaseID] - return ok -} - -// ResetLeaseID resets all changes to the "lease_id" field. -func (m *APIKeyMutation) ResetLeaseID() { - m.lease_id = nil - delete(m.clearedFields, apikey.FieldLeaseID) -} - // SetLastUsedAt sets the "last_used_at" field. func (m *APIKeyMutation) SetLastUsedAt(t time.Time) { m.last_used_at = &t @@ -1625,7 +1538,7 @@ func (m *APIKeyMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *APIKeyMutation) Fields() []string { - fields := make([]string, 0, 25) + fields := make([]string, 0, 23) if m.created_at != nil { fields = append(fields, apikey.FieldCreatedAt) } @@ -1650,12 +1563,6 @@ func (m *APIKeyMutation) Fields() []string { if m.status != nil { fields = append(fields, apikey.FieldStatus) } - if m.purpose != nil { - fields = append(fields, apikey.FieldPurpose) - } - if m.lease_id != nil { - fields = append(fields, apikey.FieldLeaseID) - } if m.last_used_at != nil { fields = append(fields, apikey.FieldLastUsedAt) } @@ -1725,10 +1632,6 @@ func (m *APIKeyMutation) Field(name string) (ent.Value, bool) { return m.GroupID() case apikey.FieldStatus: return m.Status() - case apikey.FieldPurpose: - return m.Purpose() - case apikey.FieldLeaseID: - return m.LeaseID() case apikey.FieldLastUsedAt: return m.LastUsedAt() case apikey.FieldIPWhitelist: @@ -1784,10 +1687,6 @@ func (m *APIKeyMutation) OldField(ctx context.Context, name string) (ent.Value, return m.OldGroupID(ctx) case apikey.FieldStatus: return m.OldStatus(ctx) - case apikey.FieldPurpose: - return m.OldPurpose(ctx) - case apikey.FieldLeaseID: - return m.OldLeaseID(ctx) case apikey.FieldLastUsedAt: return m.OldLastUsedAt(ctx) case apikey.FieldIPWhitelist: @@ -1883,20 +1782,6 @@ func (m *APIKeyMutation) SetField(name string, value ent.Value) error { } m.SetStatus(v) return nil - case apikey.FieldPurpose: - v, ok := value.(string) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetPurpose(v) - return nil - case apikey.FieldLeaseID: - v, ok := value.(string) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetLeaseID(v) - return nil case apikey.FieldLastUsedAt: v, ok := value.(time.Time) if !ok { @@ -2137,9 +2022,6 @@ func (m *APIKeyMutation) ClearedFields() []string { if m.FieldCleared(apikey.FieldGroupID) { fields = append(fields, apikey.FieldGroupID) } - if m.FieldCleared(apikey.FieldLeaseID) { - fields = append(fields, apikey.FieldLeaseID) - } if m.FieldCleared(apikey.FieldLastUsedAt) { fields = append(fields, apikey.FieldLastUsedAt) } @@ -2181,9 +2063,6 @@ func (m *APIKeyMutation) ClearField(name string) error { case apikey.FieldGroupID: m.ClearGroupID() return nil - case apikey.FieldLeaseID: - m.ClearLeaseID() - return nil case apikey.FieldLastUsedAt: m.ClearLastUsedAt() return nil @@ -2237,12 +2116,6 @@ func (m *APIKeyMutation) ResetField(name string) error { case apikey.FieldStatus: m.ResetStatus() return nil - case apikey.FieldPurpose: - m.ResetPurpose() - return nil - case apikey.FieldLeaseID: - m.ResetLeaseID() - return nil case apikey.FieldLastUsedAt: m.ResetLastUsedAt() return nil diff --git a/backend/ent/runtime/runtime.go b/backend/ent/runtime/runtime.go index 65526b3e64bd..b28ef960eafd 100644 --- a/backend/ent/runtime/runtime.go +++ b/backend/ent/runtime/runtime.go @@ -116,46 +116,36 @@ func init() { apikey.DefaultStatus = apikeyDescStatus.Default.(string) // apikey.StatusValidator is a validator for the "status" field. It is called by the builders before save. apikey.StatusValidator = apikeyDescStatus.Validators[0].(func(string) error) - // apikeyDescPurpose is the schema descriptor for purpose field. - apikeyDescPurpose := apikeyFields[5].Descriptor() - // apikey.DefaultPurpose holds the default value on creation for the purpose field. - apikey.DefaultPurpose = apikeyDescPurpose.Default.(string) - // apikey.PurposeValidator is a validator for the "purpose" field. It is called by the builders before save. - apikey.PurposeValidator = apikeyDescPurpose.Validators[0].(func(string) error) - // apikeyDescLeaseID is the schema descriptor for lease_id field. - apikeyDescLeaseID := apikeyFields[6].Descriptor() - // apikey.LeaseIDValidator is a validator for the "lease_id" field. It is called by the builders before save. - apikey.LeaseIDValidator = apikeyDescLeaseID.Validators[0].(func(string) error) // apikeyDescQuota is the schema descriptor for quota field. - apikeyDescQuota := apikeyFields[10].Descriptor() + apikeyDescQuota := apikeyFields[8].Descriptor() // apikey.DefaultQuota holds the default value on creation for the quota field. apikey.DefaultQuota = apikeyDescQuota.Default.(float64) // apikeyDescQuotaUsed is the schema descriptor for quota_used field. - apikeyDescQuotaUsed := apikeyFields[11].Descriptor() + apikeyDescQuotaUsed := apikeyFields[9].Descriptor() // apikey.DefaultQuotaUsed holds the default value on creation for the quota_used field. apikey.DefaultQuotaUsed = apikeyDescQuotaUsed.Default.(float64) // apikeyDescRateLimit5h is the schema descriptor for rate_limit_5h field. - apikeyDescRateLimit5h := apikeyFields[13].Descriptor() + apikeyDescRateLimit5h := apikeyFields[11].Descriptor() // apikey.DefaultRateLimit5h holds the default value on creation for the rate_limit_5h field. apikey.DefaultRateLimit5h = apikeyDescRateLimit5h.Default.(float64) // apikeyDescRateLimit1d is the schema descriptor for rate_limit_1d field. - apikeyDescRateLimit1d := apikeyFields[14].Descriptor() + apikeyDescRateLimit1d := apikeyFields[12].Descriptor() // apikey.DefaultRateLimit1d holds the default value on creation for the rate_limit_1d field. apikey.DefaultRateLimit1d = apikeyDescRateLimit1d.Default.(float64) // apikeyDescRateLimit7d is the schema descriptor for rate_limit_7d field. - apikeyDescRateLimit7d := apikeyFields[15].Descriptor() + apikeyDescRateLimit7d := apikeyFields[13].Descriptor() // apikey.DefaultRateLimit7d holds the default value on creation for the rate_limit_7d field. apikey.DefaultRateLimit7d = apikeyDescRateLimit7d.Default.(float64) // apikeyDescUsage5h is the schema descriptor for usage_5h field. - apikeyDescUsage5h := apikeyFields[16].Descriptor() + apikeyDescUsage5h := apikeyFields[14].Descriptor() // apikey.DefaultUsage5h holds the default value on creation for the usage_5h field. apikey.DefaultUsage5h = apikeyDescUsage5h.Default.(float64) // apikeyDescUsage1d is the schema descriptor for usage_1d field. - apikeyDescUsage1d := apikeyFields[17].Descriptor() + apikeyDescUsage1d := apikeyFields[15].Descriptor() // apikey.DefaultUsage1d holds the default value on creation for the usage_1d field. apikey.DefaultUsage1d = apikeyDescUsage1d.Default.(float64) // apikeyDescUsage7d is the schema descriptor for usage_7d field. - apikeyDescUsage7d := apikeyFields[18].Descriptor() + apikeyDescUsage7d := apikeyFields[16].Descriptor() // apikey.DefaultUsage7d holds the default value on creation for the usage_7d field. apikey.DefaultUsage7d = apikeyDescUsage7d.Default.(float64) accountMixin := schema.Account{}.Mixin() diff --git a/backend/ent/schema/api_key.go b/backend/ent/schema/api_key.go index a87ca851b48a..5db51270b1bf 100644 --- a/backend/ent/schema/api_key.go +++ b/backend/ent/schema/api_key.go @@ -47,16 +47,6 @@ func (APIKey) Fields() []ent.Field { field.String("status"). MaxLen(20). Default(domain.StatusActive), - field.String("purpose"). - MaxLen(32). - Default("user"). - Comment("Internal lifecycle purpose; user keys remain the public default"), - field.String("lease_id"). - MaxLen(64). - Optional(). - Nillable(). - Unique(). - Comment("Opaque idempotency identifier for short-lived internal leases"), field.Time("last_used_at"). Optional(). Nillable(). @@ -149,8 +139,6 @@ func (APIKey) Indexes() []ent.Index { index.Fields("user_id"), index.Fields("group_id"), index.Fields("status"), - index.Fields("purpose"), - index.Fields("purpose", "expires_at"), index.Fields("deleted_at"), index.Fields("last_used_at"), // Index for quota queries diff --git a/backend/internal/handler/dto/mappers.go b/backend/internal/handler/dto/mappers.go index 2b8b91428761..89f8819ed46c 100644 --- a/backend/internal/handler/dto/mappers.go +++ b/backend/internal/handler/dto/mappers.go @@ -110,10 +110,6 @@ func APIKeyFromService(k *service.APIKey) *APIKey { User: UserFromServiceShallow(k.User), Group: GroupFromServiceShallow(k.Group), } - if k.Purpose != "" && k.Purpose != service.APIKeyPurposeUser { - out.Purpose = k.Purpose - out.LeaseID = k.LeaseID - } if k.Window5hStart != nil && !service.IsWindowExpired(k.Window5hStart, service.RateLimitWindow5h) { t := k.Window5hStart.Add(service.RateLimitWindow5h) out.Reset5hAt = &t diff --git a/backend/internal/handler/dto/types.go b/backend/internal/handler/dto/types.go index 9e913c511877..fc18d690af19 100644 --- a/backend/internal/handler/dto/types.go +++ b/backend/internal/handler/dto/types.go @@ -88,11 +88,6 @@ type APIKey struct { User *User `json:"user,omitempty"` Group *Group `json:"group,omitempty"` - - // Purpose and LeaseID mark internal keys, such as the leased keys of the - // release acceptance runner. Ordinary user keys omit both. - Purpose string `json:"purpose,omitempty"` - LeaseID *string `json:"lease_id,omitempty"` } type Group struct { diff --git a/backend/internal/repository/api_key_repo.go b/backend/internal/repository/api_key_repo.go index 545b7d729b36..9060aa65fd32 100644 --- a/backend/internal/repository/api_key_repo.go +++ b/backend/internal/repository/api_key_repo.go @@ -41,19 +41,6 @@ func (r *apiKeyRepository) activeQuery() *dbent.APIKeyQuery { return r.client.APIKey.Query().Where(apikey.DeletedAtIsNil()) } -func (r *apiKeyRepository) userVisibleQuery() *dbent.APIKeyQuery { - return r.activeQuery().Where(apikey.PurposeEQ(service.APIKeyPurposeUser)) -} - -// listedQuery serves the key lists only administrators read: a user's keys in -// the admin console (ListByUserIDForAdmin), group keys and the usage search. -// It includes internal release-acceptance keys, which carry their purpose in -// the response. The owner's own key list and lookups used by mutations keep -// userVisibleQuery, so those keys never reach the user side and stay read-only. -func (r *apiKeyRepository) listedQuery() *dbent.APIKeyQuery { - return r.activeQuery() -} - func (r *apiKeyRepository) Create(ctx context.Context, key *service.APIKey) error { builder := r.client.APIKey.Create(). SetUserID(key.UserID). @@ -65,13 +52,9 @@ func (r *apiKeyRepository) Create(ctx context.Context, key *service.APIKey) erro SetQuota(key.Quota). SetQuotaUsed(key.QuotaUsed). SetNillableExpiresAt(key.ExpiresAt). - SetNillableLeaseID(key.LeaseID). SetRateLimit5h(key.RateLimit5h). SetRateLimit1d(key.RateLimit1d). SetRateLimit7d(key.RateLimit7d) - if key.Purpose != "" { - builder.SetPurpose(key.Purpose) - } if len(key.IPWhitelist) > 0 { builder.SetIPWhitelist(key.IPWhitelist) @@ -83,8 +66,6 @@ func (r *apiKeyRepository) Create(ctx context.Context, key *service.APIKey) erro created, err := builder.Save(ctx) if err == nil { key.ID = created.ID - key.Purpose = created.Purpose - key.LeaseID = created.LeaseID key.LastUsedAt = created.LastUsedAt key.CreatedAt = created.CreatedAt key.UpdatedAt = created.UpdatedAt @@ -93,7 +74,7 @@ func (r *apiKeyRepository) Create(ctx context.Context, key *service.APIKey) erro } func (r *apiKeyRepository) GetByID(ctx context.Context, id int64) (*service.APIKey, error) { - m, err := r.userVisibleQuery(). + m, err := r.activeQuery(). Where(apikey.IDEQ(id)). WithUser(). WithGroup(). @@ -113,7 +94,7 @@ func (r *apiKeyRepository) GetByID(ctx context.Context, id int64) (*service.APIK // - 不加载完整的 API Key 实体及其关联数据(User、Group 等) // - 适用于删除等只需 key 与用户 ID 的场景 func (r *apiKeyRepository) GetKeyAndOwnerID(ctx context.Context, id int64) (string, int64, error) { - m, err := r.userVisibleQuery(). + m, err := r.activeQuery(). Where(apikey.IDEQ(id)). Select(apikey.FieldKey, apikey.FieldUserID). Only(ctx) @@ -154,8 +135,6 @@ func (r *apiKeyRepository) GetByKeyForAuth(ctx context.Context, key string) (*se apikey.FieldGroupID, apikey.FieldName, apikey.FieldStatus, - apikey.FieldPurpose, - apikey.FieldLeaseID, apikey.FieldIPWhitelist, apikey.FieldIPBlacklist, apikey.FieldQuota, @@ -452,12 +431,8 @@ func (r *apiKeyRepository) deleteWithTombstone(ctx context.Context, exec *dbent. return nil } -func (r *apiKeyRepository) apiKeyListByUserIDQuery(userID int64, filters service.APIKeyListFilters, includeInternal bool) *dbent.APIKeyQuery { - q := r.userVisibleQuery() - if includeInternal { - q = r.listedQuery() - } - q = q.Where(apikey.UserIDEQ(userID)) +func (r *apiKeyRepository) apiKeyListByUserIDQuery(userID int64, filters service.APIKeyListFilters) *dbent.APIKeyQuery { + q := r.activeQuery().Where(apikey.UserIDEQ(userID)) if filters.Search != "" { q = q.Where(apikey.Or( @@ -480,17 +455,7 @@ func (r *apiKeyRepository) apiKeyListByUserIDQuery(userID int64, filters service } func (r *apiKeyRepository) ListByUserID(ctx context.Context, userID int64, params pagination.PaginationParams, filters service.APIKeyListFilters) ([]service.APIKey, *pagination.PaginationResult, error) { - return r.listByUserID(ctx, userID, params, filters, false) -} - -// ListByUserIDForAdmin is the administrator's view of one user's keys: it also -// lists internal release-acceptance keys, marked by their purpose. -func (r *apiKeyRepository) ListByUserIDForAdmin(ctx context.Context, userID int64, params pagination.PaginationParams, filters service.APIKeyListFilters) ([]service.APIKey, *pagination.PaginationResult, error) { - return r.listByUserID(ctx, userID, params, filters, true) -} - -func (r *apiKeyRepository) listByUserID(ctx context.Context, userID int64, params pagination.PaginationParams, filters service.APIKeyListFilters, includeInternal bool) ([]service.APIKey, *pagination.PaginationResult, error) { - q := r.apiKeyListByUserIDQuery(userID, filters, includeInternal) + q := r.apiKeyListByUserIDQuery(userID, filters) total, err := q.Count(ctx) if err != nil { @@ -522,7 +487,7 @@ func (r *apiKeyRepository) listByUserID(ctx context.Context, userID int64, param } func (r *apiKeyRepository) ListAllByUserID(ctx context.Context, userID int64, filters service.APIKeyListFilters) ([]service.APIKey, error) { - keys, err := r.apiKeyListByUserIDQuery(userID, filters, false). + keys, err := r.apiKeyListByUserIDQuery(userID, filters). WithGroup(). Order(dbent.Asc(apikey.FieldID)). All(ctx) @@ -639,7 +604,6 @@ func (r *apiKeyRepository) VerifyOwnership(ctx context.Context, userID int64, ap apikey.UserIDEQ(userID), apikey.IDIn(apiKeyIDs...), apikey.DeletedAtIsNil(), - apikey.PurposeEQ(service.APIKeyPurposeUser), ). IDs(ctx) if err != nil { @@ -649,7 +613,7 @@ func (r *apiKeyRepository) VerifyOwnership(ctx context.Context, userID int64, ap } func (r *apiKeyRepository) CountByUserID(ctx context.Context, userID int64) (int64, error) { - count, err := r.userVisibleQuery().Where(apikey.UserIDEQ(userID)).Count(ctx) + count, err := r.activeQuery().Where(apikey.UserIDEQ(userID)).Count(ctx) return int64(count), err } @@ -659,7 +623,7 @@ func (r *apiKeyRepository) ExistsByKey(ctx context.Context, key string) (bool, e } func (r *apiKeyRepository) ListByGroupID(ctx context.Context, groupID int64, params pagination.PaginationParams) ([]service.APIKey, *pagination.PaginationResult, error) { - q := r.listedQuery().Where(apikey.GroupIDEQ(groupID)) + q := r.activeQuery().Where(apikey.GroupIDEQ(groupID)) total, err := q.Count(ctx) if err != nil { @@ -736,7 +700,7 @@ func apiKeyListOrder(params pagination.PaginationParams) []func(*entsql.Selector // SearchAPIKeys searches API keys by user ID and/or keyword (name) func (r *apiKeyRepository) SearchAPIKeys(ctx context.Context, userID int64, keyword string, limit int) ([]service.APIKey, error) { - q := r.listedQuery() + q := r.activeQuery() if userID > 0 { q = q.Where(apikey.UserIDEQ(userID)) } @@ -763,7 +727,6 @@ func (r *apiKeyRepository) ClearGroupIDByGroupID(ctx context.Context, groupID in Where( apikey.GroupIDEQ(groupID), apikey.DeletedAtIsNil(), - apikey.PurposeEQ(service.APIKeyPurposeUser), ). ClearGroupID(). Save(ctx) @@ -778,7 +741,6 @@ func (r *apiKeyRepository) UpdateGroupIDByUserAndGroup(ctx context.Context, user apikey.UserIDEQ(userID), apikey.GroupIDEQ(oldGroupID), apikey.DeletedAtIsNil(), - apikey.PurposeEQ(service.APIKeyPurposeUser), ). SetGroupID(newGroupID). Save(ctx) @@ -787,12 +749,12 @@ func (r *apiKeyRepository) UpdateGroupIDByUserAndGroup(ctx context.Context, user // CountByGroupID 获取分组的 API Key 数量 func (r *apiKeyRepository) CountByGroupID(ctx context.Context, groupID int64) (int64, error) { - count, err := r.userVisibleQuery().Where(apikey.GroupIDEQ(groupID)).Count(ctx) + count, err := r.activeQuery().Where(apikey.GroupIDEQ(groupID)).Count(ctx) return int64(count), err } func (r *apiKeyRepository) ListKeysByUserID(ctx context.Context, userID int64) ([]string, error) { - keys, err := r.userVisibleQuery(). + keys, err := r.activeQuery(). Where(apikey.UserIDEQ(userID)). Select(apikey.FieldKey). Strings(ctx) @@ -803,7 +765,7 @@ func (r *apiKeyRepository) ListKeysByUserID(ctx context.Context, userID int64) ( } func (r *apiKeyRepository) ListKeysByGroupID(ctx context.Context, groupID int64) ([]string, error) { - keys, err := r.userVisibleQuery(). + keys, err := r.activeQuery(). Where(apikey.GroupIDEQ(groupID)). Select(apikey.FieldKey). Strings(ctx) @@ -937,8 +899,6 @@ func apiKeyEntityToService(m *dbent.APIKey) *service.APIKey { Key: m.Key, Name: m.Name, Status: m.Status, - Purpose: m.Purpose, - LeaseID: m.LeaseID, IPWhitelist: m.IPWhitelist, IPBlacklist: m.IPBlacklist, LastUsedAt: m.LastUsedAt, diff --git a/backend/internal/repository/api_key_repo_integration_test.go b/backend/internal/repository/api_key_repo_integration_test.go index 9b5a505c6457..ada0a201b3c8 100644 --- a/backend/internal/repository/api_key_repo_integration_test.go +++ b/backend/internal/repository/api_key_repo_integration_test.go @@ -86,66 +86,6 @@ func (s *APIKeyRepoSuite) TestGetByKey_NotFound() { s.Require().Error(err, "expected error for non-existent key") } -func (s *APIKeyRepoSuite) TestReleaseAcceptanceKeyIsAdminListedReadOnlyAndAuthenticates() { - user := s.mustCreateUser("release-acceptance-key@test.com") - group := s.mustCreateGroup("g-release-acceptance") - leaseID := "accept-0123456789abcdef0123456789abcdef0123456789abcdef" - key := &service.APIKey{ - UserID: user.ID, - Key: "sk-accept-integration-test", - Name: "Release acceptance (ephemeral)", - GroupID: &group.ID, - Status: service.StatusActive, - Purpose: service.APIKeyPurposeReleaseAcceptance, - LeaseID: &leaseID, - ExpiresAt: ptrTime(time.Now().Add(30 * time.Minute)), - IPWhitelist: []string{"127.0.0.1", "::1"}, - } - s.Require().NoError(s.repo.Create(s.ctx, key)) - s.Require().Equal(service.APIKeyPurposeReleaseAcceptance, key.Purpose) - - _, err := s.repo.GetByID(s.ctx, key.ID) - s.Require().ErrorIs(err, service.ErrAPIKeyNotFound) - - listed, result, err := s.repo.ListByUserID( - s.ctx, - user.ID, - pagination.PaginationParams{Page: 1, PageSize: 20}, - service.APIKeyListFilters{}, - ) - s.Require().NoError(err) - // The owner's own key list never shows the internal key. - s.Require().Empty(listed) - s.Require().Zero(result.Total) - - // Administrators see it with its purpose; it stays read-only because - // lookups used by mutations (GetByID above) do not return it. - adminListed, adminResult, err := s.repo.ListByUserIDForAdmin( - s.ctx, - user.ID, - pagination.PaginationParams{Page: 1, PageSize: 20}, - service.APIKeyListFilters{}, - ) - s.Require().NoError(err) - s.Require().Len(adminListed, 1) - s.Require().EqualValues(1, adminResult.Total) - s.Require().Equal(service.APIKeyPurposeReleaseAcceptance, adminListed[0].Purpose) - s.Require().Equal(leaseID, *adminListed[0].LeaseID) - grouped, groupResult, err := s.repo.ListByGroupID(s.ctx, group.ID, pagination.PaginationParams{Page: 1, PageSize: 20}) - s.Require().NoError(err) - s.Require().Len(grouped, 1) - s.Require().EqualValues(1, groupResult.Total) - searched, err := s.repo.SearchAPIKeys(s.ctx, user.ID, "Release acceptance", 10) - s.Require().NoError(err) - s.Require().Len(searched, 1) - - authenticated, err := s.repo.GetByKeyForAuth(s.ctx, key.Key) - s.Require().NoError(err) - s.Require().Equal(key.ID, authenticated.ID) - s.Require().Equal(service.APIKeyPurposeReleaseAcceptance, authenticated.Purpose) - s.Require().Equal(leaseID, *authenticated.LeaseID) -} - func (s *APIKeyRepoSuite) TestGetByKeyForAuth_PreservesMessagesDispatchModelConfig() { user := s.mustCreateUser("getbykey-auth-dispatch@test.com") group, err := s.client.Group.Create(). diff --git a/backend/internal/service/admin_service_apikey_test.go b/backend/internal/service/admin_service_apikey_test.go index f5348687d7fb..8abd913b94eb 100644 --- a/backend/internal/service/admin_service_apikey_test.go +++ b/backend/internal/service/admin_service_apikey_test.go @@ -565,30 +565,3 @@ func TestAdminService_AdminUpdateAPIKeyGroupID_Unbind_NoAllowedGroupUpdate(t *te require.False(t, userRepo.addGroupCalled) require.False(t, got.AutoGrantedGroupAccess) } - -type adminKeyListRepoStub struct { - APIKeyRepository - userCalls, adminCalls int -} - -func (s *adminKeyListRepoStub) ListByUserID(context.Context, int64, pagination.PaginationParams, APIKeyListFilters) ([]APIKey, *pagination.PaginationResult, error) { - s.userCalls++ - return nil, &pagination.PaginationResult{}, nil -} - -func (s *adminKeyListRepoStub) ListByUserIDForAdmin(context.Context, int64, pagination.PaginationParams, APIKeyListFilters) ([]APIKey, *pagination.PaginationResult, error) { - s.adminCalls++ - return []APIKey{{ID: 5, Purpose: APIKeyPurposeReleaseAcceptance}}, &pagination.PaginationResult{Total: 1}, nil -} - -// The admin console lists a user's internal release-acceptance keys too; the -// user's own key list (ListByUserID) does not. -func TestAdminService_GetUserAPIKeysUsesAdminListing(t *testing.T) { - repo := &adminKeyListRepoStub{} - keys, total, err := (&adminServiceImpl{apiKeyRepo: repo}).GetUserAPIKeys(context.Background(), 9, 1, 20, "created_at", "desc") - require.NoError(t, err) - require.EqualValues(t, 1, total) - require.Equal(t, APIKeyPurposeReleaseAcceptance, keys[0].Purpose) - require.Equal(t, 1, repo.adminCalls) - require.Zero(t, repo.userCalls) -} diff --git a/backend/internal/service/admin_user.go b/backend/internal/service/admin_user.go index 1d12c0f9884c..12f71f96a267 100644 --- a/backend/internal/service/admin_user.go +++ b/backend/internal/service/admin_user.go @@ -601,20 +601,9 @@ func (s *adminServiceImpl) tryAccrueAffiliateRebateForAdminRecharge(ctx context. } } -// apiKeyAdminLister lists one user's keys for administrators, internal -// release-acceptance keys included (marked by their purpose). The user's own -// key list keeps using ListByUserID, which excludes them. -type apiKeyAdminLister interface { - ListByUserIDForAdmin(ctx context.Context, userID int64, params pagination.PaginationParams, filters APIKeyListFilters) ([]APIKey, *pagination.PaginationResult, error) -} - func (s *adminServiceImpl) GetUserAPIKeys(ctx context.Context, userID int64, page, pageSize int, sortBy, sortOrder string) ([]APIKey, int64, error) { params := pagination.PaginationParams{Page: page, PageSize: pageSize, SortBy: sortBy, SortOrder: sortOrder} - list := s.apiKeyRepo.ListByUserID - if lister, ok := s.apiKeyRepo.(apiKeyAdminLister); ok { - list = lister.ListByUserIDForAdmin - } - keys, result, err := list(ctx, userID, params, APIKeyListFilters{}) + keys, result, err := s.apiKeyRepo.ListByUserID(ctx, userID, params, APIKeyListFilters{}) if err != nil { return nil, 0, err } diff --git a/backend/internal/service/api_key.go b/backend/internal/service/api_key.go index 9d499182a55a..b92a8481844b 100644 --- a/backend/internal/service/api_key.go +++ b/backend/internal/service/api_key.go @@ -12,9 +12,6 @@ const ( StatusAPIKeyDisabled = "disabled" StatusAPIKeyQuotaExhausted = "quota_exhausted" StatusAPIKeyExpired = "expired" - - APIKeyPurposeUser = "user" - APIKeyPurposeReleaseAcceptance = "release_acceptance" ) // Rate limit window durations @@ -37,8 +34,6 @@ type APIKey struct { Name string GroupID *int64 Status string - Purpose string - LeaseID *string IPWhitelist []string IPBlacklist []string // 预编译的 IP 规则,用于认证热路径避免重复 ParseIP/ParseCIDR。 diff --git a/backend/internal/service/openai_codex_context_contract.go b/backend/internal/service/openai_codex_context_contract.go deleted file mode 100644 index c23574834435..000000000000 --- a/backend/internal/service/openai_codex_context_contract.go +++ /dev/null @@ -1,135 +0,0 @@ -package service - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" -) - -const ( - // This deterministic self-test reports policy, not production model limits. - OfficialCodexContextContractSuccessLine = "CODEX_CONTEXT_CONTRACT|version=4|valid=true|priority=custom,official,upstream,registry|unknown=unchanged|aggregate=group_minimum|sentinel=preserved" - OfficialCodexContextContractFailureLine = "CODEX_CONTEXT_CONTRACT|valid=false|reason=contract-mismatch" - OfficialCodexContextInvalidArgsLine = "CODEX_CONTEXT_CONTRACT|valid=false|reason=invalid-arguments" - officialCodexContextContractSentinel = "codex-context-contract-v4" -) - -type officialCodexContextContractEnvelope struct { - Models []officialCodexContextContractModel `json:"models"` - ContractSentinel string `json:"contract_sentinel"` -} - -type officialCodexContextContractModel struct { - Slug string `json:"slug"` - ContextWindow *int64 `json:"context_window"` - MaxContextWindow *int64 `json:"max_context_window"` - MaxInputTokens *int64 `json:"max_input_tokens"` - MaxOutputTokens *int64 `json:"max_output_tokens"` - AutoCompactLimit json.RawMessage `json:"auto_compact_token_limit"` - Source string `json:"context_capacity_source"` - Reason string `json:"context_capacity_reason"` - ContractSentinel string `json:"contract_sentinel"` - UnknownCapability json.RawMessage `json:"unknown_capability"` -} - -// VerifyOfficialCodexContextContract exercises the production pure resolver, -// group aggregation and final projection without configuration, network, -// credentials, DB, or Redis. -func VerifyOfficialCodexContextContract() (string, error) { - body, err := buildOfficialCodexContextContractFixture() - if err != nil { - return "", err - } - if err := verifyNormalizedOfficialCodexContextContract(body); err != nil { - return "", err - } - return OfficialCodexContextContractSuccessLine, nil -} - -func buildOfficialCodexContextContractFixture() ([]byte, error) { - custom := modelContextEvidence{ModelContextSourceCustom, ModelContextCapacity{ContextWindow: 512000, MaxContextWindow: 512000}} - upstream := modelContextEvidence{ModelContextSourceUpstream, ModelContextCapacity{ContextWindow: 64000, MaxContextWindow: 128000, MaxInputTokens: 64000, MaxOutputTokens: 8192}} - official := modelContextEvidence{ModelContextSourceOfficial, ModelContextCapacity{ContextWindow: 272000, MaxContextWindow: 872000}} - registry := modelContextEvidence{ModelContextSourceRegistry, ModelContextCapacity{ContextWindow: 400000, MaxOutputTokens: 100000}} - relay := modelContextEvidence{ModelContextSourceUpstream, ModelContextCapacity{ContextWindow: 1050000}} - cases := []struct { - slug string - capacity ResolvedModelContextCapacity - compact int64 - }{ - {"fixture-custom", resolveModelContextEvidence([]modelContextEvidence{custom, upstream, official, registry}), 9999999}, - {"fixture-upstream", resolveModelContextEvidence([]modelContextEvidence{upstream, registry}), 50000}, - {"fixture-official", resolveModelContextEvidence([]modelContextEvidence{upstream, registry, official}), 9999999}, - {"fixture-registry", resolveModelContextEvidence([]modelContextEvidence{registry}), 9999999}, - {"fixture-unknown", resolveModelContextEvidence(nil), 666666}, - {"fixture-group", minimumModelContextCapacity([]ResolvedModelContextCapacity{ - resolveModelContextEvidence([]modelContextEvidence{relay}), - resolveModelContextEvidence([]modelContextEvidence{official}), - }), 9999999}, - } - models := make([]map[string]json.RawMessage, 0, len(cases)) - for _, test := range cases { - fields := map[string]json.RawMessage{ - "slug": json.RawMessage(fmt.Sprintf("%q", test.slug)), - "context_window": json.RawMessage("777000"), - "max_context_window": json.RawMessage("888000"), - "max_input_tokens": json.RawMessage("123000"), - "max_output_tokens": json.RawMessage("456000"), - "auto_compact_token_limit": json.RawMessage(fmt.Sprintf("%d", test.compact)), - "contract_sentinel": json.RawMessage(fmt.Sprintf("%q", officialCodexContextContractSentinel)), - "unknown_capability": json.RawMessage(`{"keep":true}`), - } - ApplyModelContextCapacityToFields(fields, test.capacity, true) - models = append(models, fields) - } - return json.Marshal(map[string]any{"models": models, "contract_sentinel": officialCodexContextContractSentinel}) -} - -func verifyNormalizedOfficialCodexContextContract(body []byte) error { - var envelope officialCodexContextContractEnvelope - if err := json.Unmarshal(body, &envelope); err != nil { - return fmt.Errorf("decode normalized manifest: %w", err) - } - if envelope.ContractSentinel != officialCodexContextContractSentinel { - return errors.New("top-level sentinel changed") - } - checks := []struct { - slug, source, reason, compact string - contextWindow, maxWindow int64 - }{ - {"fixture-custom", "custom", "", "null", 512000, 512000}, - {"fixture-upstream", "upstream", "", "50000", 128000, 128000}, - {"fixture-official", "official", "", "null", 872000, 872000}, - {"fixture-registry", "registry", "", "null", 400000, 400000}, - {"fixture-unknown", "", "", "666666", 777000, 888000}, - {"fixture-group", "official", "group_minimum", "null", 872000, 872000}, - } - if len(envelope.Models) != len(checks) { - return errors.New("model fixture count changed") - } - bySlug := make(map[string]officialCodexContextContractModel, len(envelope.Models)) - for _, model := range envelope.Models { - if _, exists := bySlug[model.Slug]; exists { - return errors.New("duplicate model fixture") - } - bySlug[model.Slug] = model - } - for _, check := range checks { - model, exists := bySlug[check.slug] - if !exists || model.ContextWindow == nil || *model.ContextWindow != check.contextWindow || - model.MaxContextWindow == nil || *model.MaxContextWindow != check.maxWindow || - model.Source != check.source || model.Reason != check.reason || - !bytes.Equal(bytes.TrimSpace(model.AutoCompactLimit), []byte(check.compact)) { - return fmt.Errorf("model %q policy mismatch", check.slug) - } - if (check.source == ModelContextSourceOfficial || check.source == ModelContextSourceCustom) && (model.MaxInputTokens != nil || model.MaxOutputTokens != nil) { - return fmt.Errorf("model %q borrowed independent limits from another source", check.slug) - } - if model.ContractSentinel != officialCodexContextContractSentinel || - !bytes.Equal(bytes.TrimSpace(model.UnknownCapability), []byte(`{"keep":true}`)) { - return errors.New("unrelated model capability changed") - } - } - return nil -} diff --git a/backend/internal/service/openai_codex_context_contract_test.go b/backend/internal/service/openai_codex_context_contract_test.go deleted file mode 100644 index a7195db461de..000000000000 --- a/backend/internal/service/openai_codex_context_contract_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package service - -import ( - "encoding/json" - "strings" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestModelContextCapacityContractUsesRuntimePolicyAndProjection(t *testing.T) { - line, err := VerifyOfficialCodexContextContract() - require.NoError(t, err) - require.Equal(t, OfficialCodexContextContractSuccessLine, line) - require.Contains(t, line, "version=4") - require.Contains(t, line, "priority=custom,official,upstream,registry") - require.NotContains(t, line, "default_context=") -} - -func TestModelContextCapacityContractFailsClosed(t *testing.T) { - body, err := buildOfficialCodexContextContractFixture() - require.NoError(t, err) - valid := string(body) - tests := []struct{ name, body string }{ - {"invalid envelope", `{"models":`}, - {"wrong custom context", strings.Replace(valid, `"context_window":512000`, `"context_window":512001`, 1)}, - {"wrong priority source", strings.Replace(valid, `"context_capacity_source":"upstream"`, `"context_capacity_source":"official"`, 1)}, - {"maximum forced to default", strings.Replace(valid, `"max_context_window":872000`, `"max_context_window":272000`, 1)}, - {"unsafe compact threshold", strings.Replace(valid, `"auto_compact_token_limit":null`, `"auto_compact_token_limit":9999999`, 1)}, - {"unknown capacity invented", strings.Replace(valid, `"context_window":777000`, `"context_window":200000`, 1)}, - {"group minimum lost", strings.Replace(valid, `"context_capacity_reason":"group_minimum"`, `"context_capacity_reason":"other"`, 1)}, - {"borrowed official output limit", strings.Replace(valid, `"slug":"fixture-official"`, `"slug":"fixture-official","max_output_tokens":8192`, 1)}, - {"sentinel changed", strings.Replace(valid, officialCodexContextContractSentinel, "changed", 1)}, - {"unknown capability changed", strings.Replace(valid, `"keep":true`, `"keep":false`, 1)}, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - require.NotEqual(t, valid, test.body, "mutation must alter the fixture") - require.Error(t, verifyNormalizedOfficialCodexContextContract([]byte(test.body))) - }) - } - var envelope map[string]json.RawMessage - require.NoError(t, json.Unmarshal(body, &envelope)) - var models []json.RawMessage - require.NoError(t, json.Unmarshal(envelope["models"], &models)) - t.Run("missing model", func(t *testing.T) { - envelope["models"], err = json.Marshal(models[1:]) - require.NoError(t, err) - missing, marshalErr := json.Marshal(envelope) - require.NoError(t, marshalErr) - require.Error(t, verifyNormalizedOfficialCodexContextContract(missing)) - }) - t.Run("duplicate model", func(t *testing.T) { - duplicated := append([]json.RawMessage(nil), models...) - duplicated[1] = duplicated[0] - envelope["models"], err = json.Marshal(duplicated) - require.NoError(t, err) - duplicate, marshalErr := json.Marshal(envelope) - require.NoError(t, marshalErr) - require.Error(t, verifyNormalizedOfficialCodexContextContract(duplicate)) - }) -} - -func TestModelContextCapacityContractAcceptsDeterministicFixture(t *testing.T) { - body, err := buildOfficialCodexContextContractFixture() - require.NoError(t, err) - require.NoError(t, verifyNormalizedOfficialCodexContextContract(body)) -} diff --git a/backend/migrations/274_purge_release_acceptance_api_keys.sql b/backend/migrations/274_purge_release_acceptance_api_keys.sql new file mode 100644 index 000000000000..25b4b99c9435 --- /dev/null +++ b/backend/migrations/274_purge_release_acceptance_api_keys.sql @@ -0,0 +1,73 @@ +-- Retire the release-acceptance key leases introduced by 239. +-- +-- Only soft-deleted release_acceptance keys with a nonempty lease are removed. +-- Their usage_logs and billing_usage_entries cascade with the key; the hot +-- and archived billing dedup claims have no foreign key and are removed by +-- the same selected key IDs. Ordinary keys and their data stay. Diagnostic +-- records in ops_error_logs, ops_system_logs and +-- ops_ingress_reject_aggregates keep their original api_key_id and content. +-- +-- Refuse an active acceptance key, an acceptance key without a lease, an +-- unknown purpose or any ordinary key carrying a lease. Nothing is removed +-- when this validation fails. Dropping both columns also removes the old +-- application's ability to authenticate keys: deploy only after the host +-- deployment tool no longer reads them. There is no old-image rollback; +-- repair a failure forward with this schema and the matching application. +-- +-- A repeated run finds neither column and changes nothing. The migration +-- runner owns the transaction; no explicit BEGIN/COMMIT belongs here. +SET LOCAL lock_timeout = '10s'; +SET LOCAL statement_timeout = '120s'; + +LOCK TABLE api_keys IN ACCESS EXCLUSIVE MODE; + +DO $$ +DECLARE + lease_columns integer; + acceptance_key_ids bigint[]; +BEGIN + SELECT count(*) INTO lease_columns + FROM pg_attribute + WHERE attrelid = 'api_keys'::regclass + AND attname IN ('purpose', 'lease_id') + AND NOT attisdropped; + + IF lease_columns = 0 THEN + RETURN; + ELSIF lease_columns <> 2 THEN + RAISE EXCEPTION 'migration 274: release-acceptance lease schema is incomplete; repair it before upgrading'; + END IF; + + IF EXISTS ( + SELECT 1 FROM api_keys + WHERE purpose IS DISTINCT FROM 'user' + AND purpose IS DISTINCT FROM 'release_acceptance' + ) OR EXISTS ( + SELECT 1 FROM api_keys + WHERE purpose = 'user' AND lease_id IS NOT NULL + ) OR EXISTS ( + SELECT 1 FROM api_keys + WHERE purpose = 'release_acceptance' + AND (deleted_at IS NULL OR lease_id IS NULL OR btrim(lease_id) = '') + ) THEN + RAISE EXCEPTION 'migration 274: release-acceptance keys are not retired leased keys, or ordinary keys carry leases; resolve the key state before upgrading'; + END IF; + + SELECT array_agg(id) INTO acceptance_key_ids + FROM api_keys + WHERE purpose = 'release_acceptance'; + + DELETE FROM usage_billing_dedup + WHERE api_key_id = ANY (acceptance_key_ids); + DELETE FROM usage_billing_dedup_archive + WHERE api_key_id = ANY (acceptance_key_ids); + DELETE FROM api_keys + WHERE id = ANY (acceptance_key_ids); + + DROP INDEX IF EXISTS api_keys_acceptance_expiry; + DROP INDEX IF EXISTS api_keys_lease_id_unique; + ALTER TABLE api_keys + DROP CONSTRAINT IF EXISTS api_keys_purpose_valid, + DROP COLUMN purpose, + DROP COLUMN lease_id; +END $$; diff --git a/backend/migrations/release_acceptance_api_key_lease_migration_test.go b/backend/migrations/release_acceptance_api_key_lease_migration_test.go deleted file mode 100644 index fab5508909d7..000000000000 --- a/backend/migrations/release_acceptance_api_key_lease_migration_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package migrations - -import ( - "strings" - "testing" -) - -func TestReleaseAcceptanceAPIKeyLeaseMigrationIsPurposeScoped(t *testing.T) { - raw, err := FS.ReadFile("239_codexrip_release_acceptance_api_key_leases.sql") - if err != nil { - t.Fatalf("read migration: %v", err) - } - sql := string(raw) - for _, required := range []string{ - "purpose VARCHAR(32) NOT NULL DEFAULT 'user'", - "lease_id VARCHAR(64)", - "CHECK (purpose IN ('user', 'release_acceptance'))", - "WHERE lease_id IS NOT NULL", - "WHERE purpose = 'release_acceptance' AND deleted_at IS NULL", - } { - if !strings.Contains(sql, required) { - t.Fatalf("migration missing %q", required) - } - } -} diff --git a/backend/migrations/release_acceptance_retirement_migration_integration_test.go b/backend/migrations/release_acceptance_retirement_migration_integration_test.go new file mode 100644 index 000000000000..3437352ae50c --- /dev/null +++ b/backend/migrations/release_acceptance_retirement_migration_integration_test.go @@ -0,0 +1,138 @@ +//go:build integration + +package migrations_test + +import ( + "context" + "database/sql" + "testing" + "time" + + dbmigrations "github.com/Wei-Shaw/sub2api/migrations" + "github.com/stretchr/testify/require" +) + +func TestMigration274RemovesReleaseAcceptanceKeysSafely(t *testing.T) { + raw, err := dbmigrations.FS.ReadFile("274_purge_release_acceptance_api_keys.sql") + require.NoError(t, err) + for _, state := range []string{"retired", "active_acceptance", "missing_lease", "ordinary_lease"} { + t.Run(state, func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) + defer cancel() + db, _ := remoteSkillMigrationTestDatabase(t) + require.NoError(t, execRemoteSkillSQL(ctx, db, releaseAcceptanceRetirementFixtureSQL)) + apply := func() error { + tx, err := db.BeginTx(ctx, nil) + if err != nil { + return err + } + defer tx.Rollback() + if _, err := tx.ExecContext(ctx, string(raw)); err != nil { + return err + } + return tx.Commit() + } + if state != "retired" { + mutation := map[string]string{ + "active_acceptance": "UPDATE api_keys SET deleted_at = NULL WHERE id = 2", + "missing_lease": "UPDATE api_keys SET lease_id = NULL WHERE id = 2", + "ordinary_lease": "UPDATE api_keys SET lease_id = 'ordinary-lease' WHERE id = 1", + }[state] + _, err := db.ExecContext(ctx, mutation) + require.NoError(t, err) + before := releaseAcceptanceRetirementSnapshot(t, ctx, db) + require.ErrorContains(t, apply(), "release-acceptance keys are not retired leased keys") + require.Equal(t, before, releaseAcceptanceRetirementSnapshot(t, ctx, db), "refusal must roll back every data and schema change") + return + } + + var ordinaryBefore, ordinaryAfter string + ordinaryQuery := `SELECT jsonb_build_object('id', id, 'key', key, 'deleted_at', deleted_at, 'xmin', xmin::text)::text FROM api_keys WHERE id = 1` + require.NoError(t, db.QueryRowContext(ctx, ordinaryQuery).Scan(&ordinaryBefore)) + ordinaryRows := map[string]string{} + for _, table := range []string{"usage_logs", "billing_usage_entries", "usage_billing_dedup", "usage_billing_dedup_archive"} { + var row string + require.NoError(t, db.QueryRowContext(ctx, "SELECT to_jsonb(t)::text FROM "+table+" t WHERE api_key_id = 1").Scan(&row)) + ordinaryRows[table] = row + } + diagnostics := map[string]string{} + for _, table := range []string{"ops_error_logs", "ops_system_logs", "ops_ingress_reject_aggregates"} { + diagnostics[table] = releaseAcceptanceRetirementTable(t, ctx, db, table) + } + require.NoError(t, apply()) + require.NoError(t, db.QueryRowContext(ctx, ordinaryQuery).Scan(&ordinaryAfter)) + require.Equal(t, ordinaryBefore, ordinaryAfter, "ordinary key values and xmin must stay") + for _, table := range []string{"api_keys", "usage_logs", "billing_usage_entries", "usage_billing_dedup", "usage_billing_dedup_archive"} { + var count int + require.NoError(t, db.QueryRowContext(ctx, "SELECT count(*) FROM "+table).Scan(&count)) + require.Equal(t, 1, count, "only the ordinary key's row remains in %s", table) + } + for table, before := range ordinaryRows { + var row string + require.NoError(t, db.QueryRowContext(ctx, "SELECT to_jsonb(t)::text FROM "+table+" t WHERE api_key_id = 1").Scan(&row)) + require.Equal(t, before, row, "ordinary key usage and claims in %s stay", table) + } + for table, before := range diagnostics { + require.Equal(t, before, releaseAcceptanceRetirementTable(t, ctx, db, table), "diagnostic key references and content in %s stay", table) + } + var nullAuditReferences int + require.NoError(t, db.QueryRowContext(ctx, "SELECT count(*) FROM prompt_audit_events WHERE api_key_id IS NULL").Scan(&nullAuditReferences)) + require.Equal(t, 2, nullAuditReferences, "SET NULL audit references must retain their rows") + var leaseObjects int + require.NoError(t, db.QueryRowContext(ctx, `SELECT + (SELECT count(*) FROM pg_attribute WHERE attrelid = 'api_keys'::regclass AND attname IN ('purpose', 'lease_id') AND NOT attisdropped) + + (SELECT count(*) FROM pg_constraint WHERE conrelid = 'api_keys'::regclass AND conname = 'api_keys_purpose_valid') + + (SELECT count(*) FROM pg_class WHERE relnamespace = (SELECT relnamespace FROM pg_class WHERE oid = 'api_keys'::regclass) AND relname IN ('api_keys_acceptance_expiry', 'api_keys_lease_id_unique'))`).Scan(&leaseObjects)) + require.Zero(t, leaseObjects, "both lease columns, dedicated indexes and constraint are gone") + after := releaseAcceptanceRetirementSnapshot(t, ctx, db) + require.NoError(t, apply()) + require.Equal(t, after, releaseAcceptanceRetirementSnapshot(t, ctx, db), "reapplying after the columns are gone is safe") + }) + } +} + +func releaseAcceptanceRetirementTable(t *testing.T, ctx context.Context, db *sql.DB, table string) string { + t.Helper() + var rows string + require.NoError(t, db.QueryRowContext(ctx, "SELECT COALESCE(jsonb_agg(to_jsonb(t) ORDER BY to_jsonb(t)::text), '[]'::jsonb)::text FROM "+table+" t").Scan(&rows)) + return rows +} + +func releaseAcceptanceRetirementSnapshot(t *testing.T, ctx context.Context, db *sql.DB) map[string]string { + t.Helper() + rows := map[string]string{} + for _, table := range []string{"api_keys", "usage_logs", "billing_usage_entries", "usage_billing_dedup", "usage_billing_dedup_archive", "ops_error_logs", "ops_system_logs", "ops_ingress_reject_aggregates", "prompt_audit_events"} { + rows[table] = releaseAcceptanceRetirementTable(t, ctx, db, table) + } + return rows +} + +const releaseAcceptanceRetirementFixtureSQL = ` +CREATE TABLE api_keys ( + id BIGINT PRIMARY KEY, key TEXT NOT NULL, purpose VARCHAR(32) NOT NULL DEFAULT 'user', + lease_id VARCHAR(64), expires_at TIMESTAMPTZ, deleted_at TIMESTAMPTZ, + CONSTRAINT api_keys_purpose_valid CHECK (purpose IN ('user', 'release_acceptance')) +); +CREATE UNIQUE INDEX api_keys_lease_id_unique ON api_keys (lease_id) WHERE lease_id IS NOT NULL; +CREATE INDEX api_keys_acceptance_expiry ON api_keys (expires_at) WHERE purpose = 'release_acceptance' AND deleted_at IS NULL; +CREATE TABLE usage_logs (id BIGINT PRIMARY KEY, api_key_id BIGINT NOT NULL REFERENCES api_keys(id) ON DELETE CASCADE, amount NUMERIC NOT NULL); +CREATE TABLE billing_usage_entries (id BIGINT PRIMARY KEY, api_key_id BIGINT NOT NULL REFERENCES api_keys(id) ON DELETE CASCADE, amount NUMERIC NOT NULL); +CREATE TABLE usage_billing_dedup (request_id TEXT NOT NULL, api_key_id BIGINT NOT NULL, PRIMARY KEY (request_id, api_key_id)); +CREATE TABLE usage_billing_dedup_archive (request_id TEXT NOT NULL, api_key_id BIGINT NOT NULL, PRIMARY KEY (request_id, api_key_id)); +CREATE TABLE ops_error_logs (id BIGINT PRIMARY KEY, api_key_id BIGINT NOT NULL, body TEXT NOT NULL); +CREATE TABLE ops_system_logs (id BIGINT PRIMARY KEY, api_key_id BIGINT NOT NULL, body TEXT NOT NULL); +CREATE TABLE ops_ingress_reject_aggregates (id BIGINT PRIMARY KEY, api_key_id BIGINT NOT NULL, body TEXT NOT NULL); +CREATE TABLE prompt_audit_events (id BIGINT PRIMARY KEY, api_key_id BIGINT REFERENCES api_keys(id) ON DELETE SET NULL, body TEXT NOT NULL); +INSERT INTO api_keys VALUES + (1, 'ordinary-key', 'user', NULL, NULL, NULL), + (2, 'retired-key-2', 'release_acceptance', 'acceptance-2', '2026-09-01', '2026-09-01'), + (3, 'retired-key-3', 'release_acceptance', 'acceptance-3', '2026-09-07', '2026-09-07'); +INSERT INTO usage_logs VALUES (1, 1, 0.5), (2, 2, 0.25), (3, 3, 0.75); +INSERT INTO billing_usage_entries SELECT * FROM usage_logs; +INSERT INTO usage_billing_dedup VALUES ('ordinary-request', 1), ('acceptance-request-2', 2), ('acceptance-request-3', 3); +INSERT INTO usage_billing_dedup_archive SELECT * FROM usage_billing_dedup; +INSERT INTO ops_error_logs VALUES (1, 1, 'ordinary-error'), (2, 2, 'acceptance-error'), (3, 3, 'acceptance-error-3'); +INSERT INTO ops_system_logs SELECT * FROM ops_error_logs; +INSERT INTO ops_ingress_reject_aggregates SELECT * FROM ops_error_logs; +INSERT INTO prompt_audit_events SELECT * FROM ops_error_logs; +` diff --git a/frontend/src/components/admin/user/UserApiKeysModal.vue b/frontend/src/components/admin/user/UserApiKeysModal.vue index be1d321e47c6..e7c2bf2bc728 100644 --- a/frontend/src/components/admin/user/UserApiKeysModal.vue +++ b/frontend/src/components/admin/user/UserApiKeysModal.vue @@ -13,8 +13,7 @@
-
{{ key.name }}{{ key.status }}{{ t('admin.users.apiKeyPurposeReadOnly', { purpose: key.purpose }) }}
-

{{ t('admin.users.apiKeyLease', { lease: key.lease_id }) }}

+
{{ key.name }}{{ key.status }}

{{ key.key.substring(0, 20) }}...{{ key.key.substring(key.key.length - 8) }}

@@ -25,7 +24,7 @@ :ref="(el) => setGroupButtonRef(key.id, el)" @click="openGroupSelector(key)" class="-mx-1 -my-0.5 flex cursor-pointer items-center gap-1 rounded-md px-1 py-0.5 transition-colors hover:bg-gray-100 disabled:cursor-default disabled:hover:bg-transparent dark:hover:bg-dark-700" - :disabled="updatingKeyIds.has(key.id) || isInternalKey(key)" + :disabled="updatingKeyIds.has(key.id)" > !!key.purpose && key.purpose !== 'user' -const apiKeys = ref([]) +const apiKeys = ref([]) const allGroups = ref([]) const loading = ref(false) let requestVersion = 0 @@ -188,8 +183,7 @@ const loadGroups = async () => { const DROPDOWN_HEIGHT = 272 // max-h-64 = 16rem = 256px + padding const DROPDOWN_GAP = 4 -const openGroupSelector = (key: AdminApiKey) => { - if (isInternalKey(key)) return +const openGroupSelector = (key: ApiKey) => { if (groupSelectorKeyId.value === key.id) { closeGroupSelector() } else { diff --git a/frontend/src/i18n/locales/en/admin/overview.ts b/frontend/src/i18n/locales/en/admin/overview.ts index ec4028653cb4..e6e07ddd6532 100644 --- a/frontend/src/i18n/locales/en/admin/overview.ts +++ b/frontend/src/i18n/locales/en/admin/overview.ts @@ -629,8 +629,6 @@ export default { apiKeys: 'API Keys', userApiKeys: 'User API Keys', noApiKeys: 'This user has no API keys', - apiKeyPurposeReadOnly: 'Internal purpose: {purpose} (read-only)', - apiKeyLease: 'Lease: {lease}', group: 'Group', none: 'None', groupChangedSuccess: 'Group updated successfully', diff --git a/frontend/src/i18n/locales/zh/admin/overview.ts b/frontend/src/i18n/locales/zh/admin/overview.ts index deec2a439f75..85426f04b63a 100644 --- a/frontend/src/i18n/locales/zh/admin/overview.ts +++ b/frontend/src/i18n/locales/zh/admin/overview.ts @@ -607,8 +607,6 @@ export default { apiKeys: 'API密钥', userApiKeys: '用户 API 密钥', noApiKeys: '此用户暂无 API 密钥', - apiKeyPurposeReadOnly: '内部用途:{purpose}(只读)', - apiKeyLease: '租约:{lease}', group: '分组', none: '无', groupChangedSuccess: '分组修改成功',