easyai-ai-gateway/apps/api/internal/store/identity_pairing.go
chengcheng 96bbd3a2f6 feat(identity): 实现接入码配对后台流程
Gateway 使用一次性接入码创建 Exchange,将 Exchange Token、机器凭据与 Session Key 仅写入 SecretStore,并持久化脱敏配对进度。\n\n资源就绪后先保存凭据和 Manifest,再自动准备 SSF,最后确认远端 Exchange;SecretStore 失败时保持 ready,重试会触发新 Secret 轮换,不回放旧值。\n\n验证:go test ./...;go vet ./...
2026-07-17 11:55:24 +08:00

62 lines
2.7 KiB
Go

package store
import (
"context"
"errors"
"github.com/easyai/easyai-ai-gateway/apps/api/internal/identity"
"github.com/jackc/pgx/v5"
)
const identityPairingColumns = `
id::text,revision_id::text,remote_exchange_id::text,exchange_token_ref,status,remote_version,expires_at,version,
COALESCE(last_error_category,''),COALESCE(auth_center_audit_id,''),COALESCE(last_trace_id,''),created_at,updated_at`
func (s *Store) CreateIdentityPairingExchange(ctx context.Context, exchange identity.PairingExchange) (identity.PairingExchange, error) {
return scanIdentityPairing(s.pool.QueryRow(ctx, `
INSERT INTO gateway_identity_onboarding_exchanges (
id,revision_id,remote_exchange_id,exchange_token_ref,status,remote_version,expires_at,last_trace_id
) VALUES ($1::uuid,$2::uuid,$3::uuid,$4,$5,$6,$7,NULLIF($8,''))
RETURNING `+identityPairingColumns,
exchange.ID, exchange.RevisionID, exchange.RemoteExchangeID, exchange.ExchangeTokenRef,
exchange.Status, exchange.RemoteVersion, exchange.ExpiresAt, exchange.LastTraceID,
))
}
func (s *Store) IdentityPairingExchange(ctx context.Context, id string) (identity.PairingExchange, error) {
exchange, err := scanIdentityPairing(s.pool.QueryRow(ctx, `SELECT `+identityPairingColumns+`
FROM gateway_identity_onboarding_exchanges WHERE id=$1::uuid`, id))
if errors.Is(err, pgx.ErrNoRows) {
return identity.PairingExchange{}, identity.ErrRevisionNotFound
}
return exchange, err
}
func (s *Store) UpdateIdentityPairingExchange(ctx context.Context, id string, expectedVersion int64, update identity.PairingExchangeUpdate) (identity.PairingExchange, error) {
exchange, err := scanIdentityPairing(s.pool.QueryRow(ctx, `
UPDATE gateway_identity_onboarding_exchanges SET status=$3,remote_version=$4,last_error_category=NULLIF($5,''),
auth_center_audit_id=COALESCE(NULLIF($6,''),auth_center_audit_id),version=version+1,updated_at=now()
WHERE id=$1::uuid AND version=$2
RETURNING `+identityPairingColumns,
id, expectedVersion, update.Status, update.RemoteVersion, update.LastErrorCategory, update.AuthCenterAuditID,
))
if errors.Is(err, pgx.ErrNoRows) {
return identity.PairingExchange{}, identity.ErrRevisionConflict
}
return exchange, err
}
func scanIdentityPairing(row scanner) (identity.PairingExchange, error) {
var exchange identity.PairingExchange
var status string
if err := row.Scan(
&exchange.ID, &exchange.RevisionID, &exchange.RemoteExchangeID, &exchange.ExchangeTokenRef, &status,
&exchange.RemoteVersion, &exchange.ExpiresAt, &exchange.Version, &exchange.LastErrorCategory,
&exchange.AuthCenterAuditID, &exchange.LastTraceID, &exchange.CreatedAt, &exchange.UpdatedAt,
); err != nil {
return identity.PairingExchange{}, err
}
exchange.Status = identity.PairingStatus(status)
return exchange, nil
}