chore(merge): 整合最新 main 与统一认证变更
ci / verify (pull_request) Successful in 12m18s

合并远端生产 CI、依赖与 API 文档改动,保留统一认证和 SSF 能力。由于远端生产基线已占用 0062,将尚未发布的身份迁移整理为 0063 至 0068 的最终增量 Schema,移除仅用于开发期草稿升级的破坏性迁移步骤。

验证:go test ./...。其余生产门禁在合并提交后继续执行。
This commit is contained in:
2026-07-17 19:06:37 +08:00
100 changed files with 9673 additions and 1799 deletions
@@ -686,14 +686,10 @@ func newIdentityPairingPostgresTestStore(t *testing.T) *Store {
for _, migrationName := range []string{
"0001_init.sql",
"0061_oidc_server_sessions.sql",
"0067_identity_configuration_revisions.sql",
"0068_identity_onboarding_exchanges.sql",
"0069_identity_pairing_cancellation.sql",
"0070_identity_secret_cleanup_queue.sql",
"0071_identity_pairing_start_reservation.sql",
"0072_identity_secret_cleanup_claim_lifecycle.sql",
"0073_identity_pairing_start_reservation_upgrade.sql",
"0074_identity_pairing_error_categories.sql",
"0065_identity_configuration_revisions.sql",
"0066_identity_onboarding_exchanges.sql",
"0067_identity_secret_cleanup_queue.sql",
"0068_identity_pairing_start_reservation.sql",
} {
migration, err := os.ReadFile(filepath.Join(migrationDirectory, migrationName))
if err != nil {
@@ -16,81 +16,53 @@ import (
"github.com/jackc/pgx/v5/pgxpool"
)
func TestIdentitySecretCleanupClaimLifecycleUpgradeMigrationExists(t *testing.T) {
payload, err := os.ReadFile(identitySecretCleanupMigrationPath(t, "0072_identity_secret_cleanup_claim_lifecycle.sql"))
const identitySecretCleanupMigration = "0067_identity_secret_cleanup_queue.sql"
func TestIdentitySecretCleanupMigrationDefinesClaimLifecycle(t *testing.T) {
payload, err := os.ReadFile(identitySecretCleanupMigrationPath(t, identitySecretCleanupMigration))
if err != nil {
t.Fatal(err)
}
content := strings.ToLower(string(payload))
for _, required := range []string{
"add column if not exists status text",
"add column if not exists claim_token uuid",
"add column if not exists lease_expires_at timestamptz",
"alter column status set default 'pending'",
"alter column status set not null",
"gateway_identity_secret_cleanup_status_check",
"create table if not exists gateway_identity_secret_cleanup_queue",
"secret_ref text primary key",
"status text not null default 'pending'",
"claim_token uuid",
"lease_expires_at timestamptz",
"gateway_identity_secret_cleanup_claim_check",
"drop index if exists idx_gateway_identity_secret_cleanup_due",
"create index idx_gateway_identity_secret_cleanup_due",
"idx_gateway_identity_secret_cleanup_due",
} {
if !strings.Contains(content, required) {
t.Fatalf("identity Secret cleanup lifecycle migration is missing %q", required)
t.Fatalf("identity Secret cleanup migration is missing %q", required)
}
}
for _, forbidden := range []string{"secret_value", "client_secret", "machine_secret", "token text"} {
for _, forbidden := range []string{"secret_value", "client_secret", "machine_secret", "token text", "drop ", "set not null"} {
if strings.Contains(content, forbidden) {
t.Fatalf("identity Secret cleanup lifecycle migration stores forbidden value field %q", forbidden)
t.Fatalf("identity Secret cleanup migration contains forbidden content %q", forbidden)
}
}
}
func TestIdentitySecretCleanupClaimLifecycleUpgradeMigratesLegacyQueue(t *testing.T) {
func TestIdentitySecretCleanupMigrationSupportsClaimLifecycle(t *testing.T) {
db := newIdentitySecretCleanupMigrationPostgresStore(t)
ctx := context.Background()
reference := "identity-cleanup-legacy-" + uuid.NewString()
applyIdentitySecretCleanupMigration(t, ctx, db.pool, identitySecretCleanupMigration)
reference := "identity-cleanup-" + uuid.NewString()
if _, err := db.pool.Exec(ctx, `
CREATE TABLE gateway_identity_secret_cleanup_queue (
secret_ref text PRIMARY KEY CHECK (secret_ref ~ '^[a-z0-9][a-z0-9-]{0,127}$'),
not_before timestamptz NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);`); err != nil {
t.Fatalf("create legacy identity Secret cleanup queue: %v", err)
if err := db.QueueIdentitySecretCleanup(ctx, reference, time.Now().Add(-time.Minute)); err != nil {
t.Fatalf("queue identity Secret cleanup: %v", err)
}
if _, err := db.pool.Exec(ctx, `CREATE INDEX idx_gateway_identity_secret_cleanup_due
ON gateway_identity_secret_cleanup_queue(not_before,updated_at)`); err != nil {
t.Fatalf("create legacy identity Secret cleanup due index: %v", err)
}
if _, err := db.pool.Exec(ctx, `INSERT INTO gateway_identity_secret_cleanup_queue(secret_ref,not_before)
VALUES($1,now()-interval '1 minute')`, reference); err != nil {
t.Fatalf("seed legacy identity Secret cleanup row: %v", err)
}
applyIdentitySecretCleanupMigration(t, ctx, db.pool, "0072_identity_secret_cleanup_claim_lifecycle.sql")
var status string
var claimToken, leaseExpiresAt *string
if err := db.pool.QueryRow(ctx, `
SELECT status,claim_token::text,lease_expires_at::text
FROM gateway_identity_secret_cleanup_queue WHERE secret_ref=$1`, reference).
Scan(&status, &claimToken, &leaseExpiresAt); err != nil {
t.Fatalf("read migrated cleanup row: %v", err)
}
if status != "pending" || claimToken != nil || leaseExpiresAt != nil {
t.Fatalf("legacy cleanup row was not backfilled to an unclaimed pending lifecycle")
}
claims, err := db.ClaimIdentitySecretCleanups(ctx, 1, time.Minute)
if err != nil {
t.Fatalf("claim migrated cleanup row: %v", err)
t.Fatalf("claim identity Secret cleanup: %v", err)
}
if len(claims) != 1 || claims[0].Reference != reference || claims[0].ClaimToken == "" {
t.Fatalf("claim migrated cleanup row returned unexpected claim metadata")
t.Fatalf("claim identity Secret cleanup returned unexpected metadata: %#v", claims)
}
completed, err := db.CompleteIdentitySecretCleanup(ctx, reference, claims[0].ClaimToken)
if err != nil || !completed {
t.Fatalf("complete migrated cleanup claim: completed=%t err=%v", completed, err)
t.Fatalf("complete identity Secret cleanup: completed=%t err=%v", completed, err)
}
invalidReference := "identity-cleanup-invalid-" + uuid.NewString()
@@ -105,15 +77,11 @@ INSERT INTO gateway_identity_secret_cleanup_queue(
SELECT pg_get_indexdef(indexrelid)
FROM pg_index
WHERE indexrelid='idx_gateway_identity_secret_cleanup_due'::regclass`).Scan(&indexDefinition); err != nil {
t.Fatalf("read upgraded cleanup due index: %v", err)
t.Fatalf("read cleanup due index: %v", err)
}
if !strings.Contains(strings.ToLower(indexDefinition), "(status, not_before, lease_expires_at, updated_at)") {
t.Fatalf("cleanup due index does not cover the claim lifecycle")
t.Fatalf("cleanup due index does not cover the claim lifecycle: %q", indexDefinition)
}
// The production migration runner applies each version once. Reapplying here
// proves that the upgrade also accepts the already-current 0070 schema shape.
applyIdentitySecretCleanupMigration(t, ctx, db.pool, "0072_identity_secret_cleanup_claim_lifecycle.sql")
}
func newIdentitySecretCleanupMigrationPostgresStore(t *testing.T) *Store {
@@ -125,12 +93,12 @@ func newIdentitySecretCleanupMigrationPostgresStore(t *testing.T) *Store {
ctx := context.Background()
admin, err := pgxpool.New(ctx, databaseURL)
if err != nil {
t.Fatalf("connect identity Secret cleanup migration test database: %v", err)
t.Fatalf("connect identity migration test database: %v", err)
}
var databaseName string
if err := admin.QueryRow(ctx, `SELECT current_database()`).Scan(&databaseName); err != nil {
admin.Close()
t.Fatalf("read identity Secret cleanup migration test database name: %v", err)
t.Fatalf("read identity migration test database name: %v", err)
}
if !strings.Contains(strings.ToLower(databaseName), "test") {
admin.Close()
@@ -141,25 +109,25 @@ func newIdentitySecretCleanupMigrationPostgresStore(t *testing.T) *Store {
schemaIdentifier := pgx.Identifier{schemaName}.Sanitize()
if _, err := admin.Exec(ctx, `CREATE SCHEMA `+schemaIdentifier); err != nil {
admin.Close()
t.Fatalf("create identity Secret cleanup migration test schema: %v", err)
t.Fatalf("create identity migration test schema: %v", err)
}
config, err := pgxpool.ParseConfig(databaseURL)
if err != nil {
_, _ = admin.Exec(ctx, `DROP SCHEMA IF EXISTS `+schemaIdentifier+` CASCADE`)
admin.Close()
t.Fatalf("parse identity Secret cleanup migration test database URL: %v", err)
t.Fatalf("parse identity migration test database URL: %v", err)
}
config.ConnConfig.RuntimeParams["search_path"] = schemaName
pool, err := pgxpool.NewWithConfig(ctx, config)
if err != nil {
_, _ = admin.Exec(ctx, `DROP SCHEMA IF EXISTS `+schemaIdentifier+` CASCADE`)
admin.Close()
t.Fatalf("connect identity Secret cleanup migration test schema: %v", err)
t.Fatalf("connect identity migration test schema: %v", err)
}
t.Cleanup(func() {
pool.Close()
if _, err := admin.Exec(context.Background(), `DROP SCHEMA IF EXISTS `+schemaIdentifier+` CASCADE`); err != nil {
t.Errorf("drop identity Secret cleanup migration test schema: %v", err)
t.Errorf("drop identity migration test schema: %v", err)
}
admin.Close()
})
@@ -3,6 +3,8 @@ package store
import (
"context"
"encoding/json"
"fmt"
"math"
"strings"
"github.com/jackc/pgx/v5"
@@ -116,6 +118,9 @@ func (s *Store) createPlatformModel(ctx context.Context, q platformModelQuerier,
if len(capabilities) == 0 {
capabilities = EffectivePlatformModelCapabilities(base.Capabilities, input.CapabilityOverride)
}
if err := validateEnabledVolcesTextModelCapabilities(ctx, q, input, capabilities); err != nil {
return PlatformModel{}, err
}
billingConfig := input.BillingConfig
if len(billingConfig) == 0 {
billingConfig = mergeObjects(base.BaseBillingConfig, input.BillingConfigOverride)
@@ -262,6 +267,76 @@ RETURNING id::text, platform_id::text, COALESCE(base_model_id::text, ''), model_
return model, nil
}
func validateEnabledVolcesTextModelCapabilities(ctx context.Context, q platformModelQuerier, input CreatePlatformModelInput, capabilities map[string]any) error {
// createPlatformModel enables/upserts models unconditionally, so every text
// model that reaches this path must already satisfy the Volcengine invariant.
if !containsTextOutputModelType(input.ModelType) {
return nil
}
var provider string
var baseURL string
if err := q.QueryRow(ctx, `SELECT provider, COALESCE(base_url, '') FROM integration_platforms WHERE id = $1::uuid`, input.PlatformID).Scan(&provider, &baseURL); err != nil {
return err
}
provider = strings.ToLower(strings.TrimSpace(provider))
baseURL = strings.ToLower(strings.TrimSpace(baseURL))
if provider != "volces-openai" && !strings.Contains(baseURL, "volces.com") && !strings.Contains(baseURL, "byteplus.com") {
return nil
}
seen := map[string]struct{}{}
for _, modelType := range append(append(StringList{}, input.ModelType...), "text_generate") {
modelType = strings.TrimSpace(modelType)
if modelType == "" {
continue
}
if _, ok := seen[modelType]; ok {
continue
}
seen[modelType] = struct{}{}
capability, _ := capabilities[modelType].(map[string]any)
if value, ok := positiveWholeNumber(capability["max_output_tokens"]); ok && value > 0 {
return nil
}
}
return fmt.Errorf("%w: enabled Volcengine text model %q requires a positive integer max_output_tokens capability for its text model type or text_generate fallback", ErrInvalidPlatformModelConfiguration, input.ProviderModelName)
}
func containsTextOutputModelType(values StringList) bool {
for _, value := range values {
switch strings.ToLower(strings.TrimSpace(value)) {
case "text_generate", "chat", "responses", "text":
return true
}
}
return false
}
func positiveWholeNumber(value any) (int64, bool) {
var number float64
switch typed := value.(type) {
case int:
number = float64(typed)
case int32:
number = float64(typed)
case int64:
number = float64(typed)
case float64:
number = typed
case json.Number:
parsed, err := typed.Float64()
if err != nil {
return 0, false
}
number = parsed
default:
return 0, false
}
if number <= 0 || math.Trunc(number) != number || number > math.MaxInt64 {
return 0, false
}
return int64(number), true
}
func (s *Store) DeletePlatformModel(ctx context.Context, id string) error {
tx, err := s.pool.Begin(ctx)
if err != nil {
+3 -2
View File
@@ -7,8 +7,9 @@ import (
)
var (
ErrNoModelCandidate = errors.New("no enabled platform model matches request")
ErrRateLimited = errors.New("rate limit exceeded")
ErrNoModelCandidate = errors.New("no enabled platform model matches request")
ErrRateLimited = errors.New("rate limit exceeded")
ErrInvalidPlatformModelConfiguration = errors.New("invalid platform model configuration")
)
type ModelCandidateUnavailableError struct {