feat: add Z12/Z13 bind APIs, stock import, and sync docs
Enable auto default sync channels on agent activate, bind-code/phone confirm flows, publish ALTER, and align admin/yuheng docs with the production bind path.
This commit is contained in:
@@ -79,6 +79,10 @@ type Store interface {
|
||||
HasAppAccess(ctx context.Context, agentID int64, slug string) (bool, error)
|
||||
// GrantAppSlug 将 slug 写入智能体可访问模块(幂等)。新建发布时自动授权用。
|
||||
GrantAppSlug(ctx context.Context, agentID int64, slug string) error
|
||||
// FindByHostKey 按宿主机 key 查找(跨租户,Z13 绑定用)。
|
||||
FindByHostKey(ctx context.Context, hostKey string) (*Account, error)
|
||||
// AttachSyncBind 写入同步落点;可改挂租户并激活(Z12/Z13)。
|
||||
AttachSyncBind(ctx context.Context, agentID, tenantID int64, channelID, onlineDBID, databaseName string, activate bool) (*Account, error)
|
||||
}
|
||||
|
||||
type memAcc struct {
|
||||
@@ -341,6 +345,42 @@ func (s *MemoryStore) GrantAppSlug(_ context.Context, agentID int64, slug string
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *MemoryStore) FindByHostKey(_ context.Context, hostKey string) (*Account, error) {
|
||||
hostKey = strings.TrimSpace(hostKey)
|
||||
if hostKey == "" {
|
||||
return nil, fmt.Errorf("host_key required")
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
for _, a := range s.byID {
|
||||
if a.HostKey == hostKey {
|
||||
cp := cloneAcc(&a.Account)
|
||||
return &cp, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("agent not found")
|
||||
}
|
||||
|
||||
func (s *MemoryStore) AttachSyncBind(_ context.Context, agentID, tenantID int64, channelID, onlineDBID, databaseName string, activate bool) (*Account, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
a, ok := s.byID[agentID]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("agent not found")
|
||||
}
|
||||
if tenantID > 0 {
|
||||
a.TenantID = tenantID
|
||||
}
|
||||
a.ChannelID = strings.TrimSpace(channelID)
|
||||
a.OnlineDBID = strings.TrimSpace(onlineDBID)
|
||||
a.DatabaseName = strings.TrimSpace(databaseName)
|
||||
if activate {
|
||||
a.Status = StatusActive
|
||||
}
|
||||
cp := cloneAcc(&a.Account)
|
||||
return &cp, nil
|
||||
}
|
||||
|
||||
type PostgresStore struct {
|
||||
DB *sql.DB
|
||||
}
|
||||
@@ -653,6 +693,59 @@ INSERT INTO platform_meta.agent_app_grants(agent_id, slug) VALUES($1,$2)`, agent
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *PostgresStore) FindByHostKey(ctx context.Context, hostKey string) (*Account, error) {
|
||||
hostKey = strings.TrimSpace(hostKey)
|
||||
if hostKey == "" {
|
||||
return nil, fmt.Errorf("host_key required")
|
||||
}
|
||||
var a Account
|
||||
var last sql.NullTime
|
||||
err := s.DB.QueryRowContext(ctx, `
|
||||
SELECT agent_id, tenant_id, name, client_id, COALESCE(host_key,''), COALESCE(role_id,0),
|
||||
COALESCE(channel_id,''), COALESCE(online_db_id,''), COALESCE(database_name,''),
|
||||
status, created_by, created_at, last_token_at
|
||||
FROM platform_meta.agent_accounts WHERE host_key=$1
|
||||
ORDER BY agent_id DESC LIMIT 1`, hostKey,
|
||||
).Scan(&a.AgentID, &a.TenantID, &a.Name, &a.ClientID, &a.HostKey, &a.RoleID,
|
||||
&a.ChannelID, &a.OnlineDBID, &a.DatabaseName,
|
||||
&a.Status, &a.CreatedBy, &a.CreatedAt, &last)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, fmt.Errorf("agent not found")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if last.Valid {
|
||||
t := last.Time
|
||||
a.LastTokenAt = &t
|
||||
}
|
||||
perms, slugs, err := s.loadKids(ctx, a.AgentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a.Perms, a.AppSlugs = perms, slugs
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
func (s *PostgresStore) AttachSyncBind(ctx context.Context, agentID, tenantID int64, channelID, onlineDBID, databaseName string, activate bool) (*Account, error) {
|
||||
statusSQL := ""
|
||||
args := []any{tenantID, strings.TrimSpace(channelID), strings.TrimSpace(onlineDBID), strings.TrimSpace(databaseName), agentID}
|
||||
if activate {
|
||||
statusSQL = ", status='active'"
|
||||
}
|
||||
res, err := s.DB.ExecContext(ctx, `
|
||||
UPDATE platform_meta.agent_accounts SET tenant_id=$1, channel_id=$2, online_db_id=$3, database_name=$4`+statusSQL+`
|
||||
WHERE agent_id=$5`, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n, _ := res.RowsAffected()
|
||||
if n == 0 {
|
||||
return nil, fmt.Errorf("agent not found")
|
||||
}
|
||||
return s.Get(ctx, tenantID, agentID)
|
||||
}
|
||||
|
||||
func (s *PostgresStore) loadKids(ctx context.Context, agentID int64) ([]string, []string, error) {
|
||||
prows, err := s.DB.QueryContext(ctx, `SELECT perm FROM platform_meta.agent_permissions WHERE agent_id=$1`, agentID)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user