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
+58
-24
@@ -114,6 +114,40 @@ type capacityEmitter interface {
|
||||
BroadcastCapacityAwaken(sessionID, fromState, toState, tool string)
|
||||
}
|
||||
|
||||
// ownerRegistrar implements gateway.OwnerRegistrar: it persists a channel account
|
||||
// id into the owner whitelist (configs/users/owner.yaml) AND hot-updates the live
|
||||
// owner profile the channel bridge holds, so a weixin scan-login confirmation is
|
||||
// recognized as the owner immediately — no restart, no manual edit. path and the
|
||||
// live profile are captured at wiring time: the boot-loaded *user.OwnerProfile is
|
||||
// the same pointer the bridge resolves actors against, so RegisterAccount on it is
|
||||
// visible to ResolveActor without touching the bridge.
|
||||
type ownerRegistrar struct {
|
||||
path string
|
||||
owner *user.OwnerProfile
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
func (r *ownerRegistrar) RegisterChannelOwner(_ context.Context, platform, accountID string) error {
|
||||
if strings.TrimSpace(accountID) == "" {
|
||||
return nil
|
||||
}
|
||||
if err := user.RegisterOwnerAccount(r.path, platform, accountID); err != nil {
|
||||
return fmt.Errorf("register owner %s:%s: %w", platform, accountID, err)
|
||||
}
|
||||
// Hot-effect: extend the live profile the bridge already points at. If the
|
||||
// profile failed to load at boot (r.owner nil) the persist above still lands
|
||||
// on disk and is recognized after a restart; log so the operator knows the
|
||||
// hot path was unavailable.
|
||||
if r.owner != nil {
|
||||
r.owner.RegisterAccount(platform, accountID)
|
||||
} else {
|
||||
r.log.Warn("owner registrar: no live profile, whitelist persisted to disk only (restart to take effect)",
|
||||
"platform", platform, "accountId", accountID)
|
||||
}
|
||||
r.log.Info("owner whitelist registered", "platform", platform, "accountId", accountID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// newApp assembles the full Cyrene runtime from cfg and log. It opens the
|
||||
// database, migrates the schema, wires the persona registry, model adapter, tool
|
||||
// dispatcher, memory stack, agent loop, orchestration, execution, IoT, channel
|
||||
@@ -625,29 +659,30 @@ func (a *app) wireFrontend() error {
|
||||
// and the gateway share the same persistent store. History was a no-op dependency
|
||||
// before the IM path, so nothing downstream depends on its creation order change.
|
||||
gw, err := gateway.New(a.cfg, gateway.Deps{
|
||||
Log: logger.WithSource(a.log, "gateway"),
|
||||
Auth: jwt,
|
||||
Bus: a.bus,
|
||||
Registry: a.caps,
|
||||
Loop: a.loop,
|
||||
Session: a.sessions,
|
||||
Personas: a.personas,
|
||||
Memories: a.memStore,
|
||||
History: a.history,
|
||||
IoT: a.iot,
|
||||
Scheduler: a.scheduler,
|
||||
Tasks: a.tasks,
|
||||
Automations: a.automation,
|
||||
Users: apiKeys,
|
||||
Backthink: a.backthink,
|
||||
RAG: ragStore,
|
||||
LLM: a.llm,
|
||||
Channels: a.channels,
|
||||
AuditStore: auditStore,
|
||||
Streaming: true,
|
||||
Frontend: resolveFrontendFS(a.log),
|
||||
Webhooks: a.channelBridge.WebhookHandlers(),
|
||||
DatabasePath: a.dbPath,
|
||||
Log: logger.WithSource(a.log, "gateway"),
|
||||
Auth: jwt,
|
||||
Bus: a.bus,
|
||||
Registry: a.caps,
|
||||
Loop: a.loop,
|
||||
Session: a.sessions,
|
||||
Personas: a.personas,
|
||||
Memories: a.memStore,
|
||||
History: a.history,
|
||||
IoT: a.iot,
|
||||
Scheduler: a.scheduler,
|
||||
Tasks: a.tasks,
|
||||
Automations: a.automation,
|
||||
Users: apiKeys,
|
||||
Backthink: a.backthink,
|
||||
RAG: ragStore,
|
||||
LLM: a.llm,
|
||||
Channels: a.channels,
|
||||
OwnerRegistrar: &ownerRegistrar{path: defaultOwnerPath, owner: a.owner, log: a.log},
|
||||
AuditStore: auditStore,
|
||||
Streaming: true,
|
||||
Frontend: resolveFrontendFS(a.log),
|
||||
Webhooks: a.channelBridge.WebhookHandlers(),
|
||||
DatabasePath: a.dbPath,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("gateway: %w", err)
|
||||
@@ -668,7 +703,6 @@ func (a *app) wireFrontend() error {
|
||||
}
|
||||
a.capacitySink = gw
|
||||
|
||||
|
||||
// Wire the backthink proactive sinks now that the gateway hub (active-client
|
||||
// route) and the channel bridge (IM fallback) both exist. They were absent when
|
||||
// backthink.New ran (the hub needs the loop and the bridge needs the loop and the
|
||||
|
||||
Reference in new issue
Block a user