feat(owner): 扫码绑定确认时自动把主人id登记进owner.yaml白名单(热生效)
This commit is contained in:
1 parent
0aa387f83c
commit
5a7ee9d772
7 files changed
+487
-65
No files matched your search
+42
-2
@@ -4,6 +4,8 @@
|
||||
// this whitelist — and is never driven by prompt text or an LLM judgment.
|
||||
package user
|
||||
|
||||
import "sync"
|
||||
|
||||
// OwnerAccount is a single channel-fingerprint that belongs to the owner. The
|
||||
// policy engine matches channel-layer actor metadata (platform + account id)
|
||||
// against this list.
|
||||
@@ -16,7 +18,14 @@ type OwnerAccount struct {
|
||||
// persona bound to the owner, and the per-channel account whitelist. It is the
|
||||
// source data for owner recognition; it is not injected into the persona's
|
||||
// prompt as an authority.
|
||||
//
|
||||
// mu guards Accounts so a live profile can be hot-extended (RegisterAccount)
|
||||
// while the channel bridge's ResolveActor reads it (IsOwner) concurrently. The
|
||||
// mutex is unexported and carries no YAML tag, so it is invisible to encode and
|
||||
// is not part of the persisted profile.
|
||||
type OwnerProfile struct {
|
||||
mu sync.RWMutex `yaml:"-"`
|
||||
|
||||
ID string `yaml:"id"`
|
||||
DisplayName string `yaml:"displayName"`
|
||||
DefaultPersona string `yaml:"defaultPersona"`
|
||||
@@ -28,8 +37,15 @@ type OwnerProfile struct {
|
||||
}
|
||||
|
||||
// IsOwner reports whether the given (platform, accountID) pair belongs to the
|
||||
// owner according to the whitelist.
|
||||
// owner according to the whitelist. It is safe for concurrent use with
|
||||
// RegisterAccount (the bridge reads this on every inbound message while a
|
||||
// scan-login registration may extend the whitelist).
|
||||
func (p *OwnerProfile) IsOwner(platform, accountID string) bool {
|
||||
if p == nil {
|
||||
return false
|
||||
}
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
for _, a := range p.Accounts {
|
||||
if a.Platform == platform && a.AccountID == accountID {
|
||||
return true
|
||||
@@ -38,9 +54,33 @@ func (p *OwnerProfile) IsOwner(platform, accountID string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// RegisterAccount appends (platform, accountID) to the whitelist if it is not
|
||||
// already present, leaving the profile otherwise untouched. It is the in-memory
|
||||
// counterpart of RegisterOwnerAccount: the channel bridge holds the very same
|
||||
// *OwnerProfile, so a scan-login confirmation can extend the whitelist without a
|
||||
// restart. Duplicate (platform, accountID) pairs are a no-op, matching IsOwner.
|
||||
func (p *OwnerProfile) RegisterAccount(platform, accountID string) {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
for _, a := range p.Accounts {
|
||||
if a.Platform == platform && a.AccountID == accountID {
|
||||
return
|
||||
}
|
||||
}
|
||||
p.Accounts = append(p.Accounts, OwnerAccount{Platform: platform, AccountID: accountID})
|
||||
}
|
||||
|
||||
// AccountIdentifiers returns the distinct channel account ids owned by the
|
||||
// owner, for diagnostics / listing.
|
||||
// owner, for diagnostics / listing. Safe for concurrent use with RegisterAccount.
|
||||
func (p *OwnerProfile) AccountIdentifiers() []string {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
seen := make(map[string]struct{}, len(p.Accounts))
|
||||
out := make([]string, 0, len(p.Accounts))
|
||||
for _, a := range p.Accounts {
|
||||
|
||||
Reference in new issue
Block a user