连接器 — 图示覆盖(注册侧 vs 调用侧)
父级:connector
连接器模块是共享一个包的两套设计:注册侧(连接器如何被安装、授予凭据、激活)和调用侧(消费者如何在完全看不到提供方或凭据的情况下调用一个类别动词)。用一张图恰好会模糊掉这个模块存在的意义所系的那条边界——所以,画两张。
1 · 注册侧 — 安装、凭据、激活
classDiagram
class connectorService {
<<connector.Service - the admin-facing side, service.go + svc_*.go>>
Repo.SaveUploaded / Service.UpdateUploaded - plugin upload
connect / oauth / activate / disconnect
credential seal-unseal via cryptobox
}
class Hub {
-conns map[string]Connector
-mu sync.RWMutex
+Register(c Connector)
+Upsert(c Connector)
+Resolve(name) (Connector, bool)
}
class Connector {
<<interface>>
+Name() string
+Kind() string
+Connected(ctx, ownerID) (bool, error)
}
class Verifier {
<<interface - optional>>
+Verify(ctx, ownerID) error
}
class cryptobox {
AES-GCM seal and open
creds ciphertext at rest
}
class owner_connectors {
<<postgres>>
per-owner connection rows
credential ciphertext lives here
}
connectorService --> Hub : Register / Upsert on activate
Hub o-- Connector : by name
Connector <|-- Verifier : optionally also
connectorService ..> cryptobox : seal creds
cryptobox ..> owner_connectors : ciphertext
注册侧是凭据以明文形式存在的唯一地方,而且只是暂时的。Hub.Resolve 下游的一切看到的都只是一个 Connector——名称、类别、是否已连接;别无其他。
2 · 调用侧 — 类别动词,没有提供方,没有凭据
classDiagram
class Consumer {
agent tool / platform feature
future IM-gateway, job-loop
}
class CalendarProxy {
<<interface - connector/contract>>
+Connected(ctx, ownerID) (bool, error)
+FreeBusy(ctx, ownerID, FreeBusyReq) ([]BusyInterval, error)
+InsertEvent(ctx, ownerID, *InsertEventReq) (InsertedEvent, error)
+DeleteEvent(ctx, ownerID, eventID, attendeeEmail) error
}
class MailProxy {
<<interface - connector/contract>>
+Connected(ctx, ownerID) (bool, error)
+Send(ctx, ownerID, MailMessage) (MailReceipt, error)
}
class DepRegistry {
+AllConnected(ctx, ownerID, names) (bool, error)
gate BEFORE exposure - fail-closed
}
class openapiRuntime {
-spec *Spec
-binding *Binding - JSONata
-doer Doer
-baseURL string
+Call(ctx, op, input, dst, AuthInjector) error
}
class AuthInjector {
<<func type>>
func(req *http.Request) error
built inside the connector pkg per call
}
class protocolImpl {
built-in Go clients - SMTP, CalDAV
telegram - token vault only, no contract
}
class StatusError {
Code int
Transient bool - retry per call-class
}
Consumer ..> DepRegistry : exposure gated first
Consumer --> CalendarProxy : category verbs only
Consumer --> MailProxy : category verbs only
CalendarProxy <|.. openapiRuntime
CalendarProxy <|.. protocolImpl
MailProxy <|.. openapiRuntime
MailProxy <|.. protocolImpl
openapiRuntime ..> AuthInjector : injected per request
openapiRuntime --> StatusError : transient vs fatal
调用方的世界里没有提供方名称,也没有凭据——代理接口(proxy interfaces)就是全部词汇。AuthInjector 是在包内部按调用时构造的;消费者甚至无法叫出它的名字。
覆盖清单(防遗漏关卡)
| 类型 / 区域 | 图示位置 | 已验证 |
|---|---|---|
| Hub、Connector、Verifier | 本页 §1 | ✓ 代码 |
connector.Service 的职责(2026-07-26 之前叫 connectorsvc,1bc9ba8b0) |
本页 §1 | ✓ 名称,☐ 完整签名 |
| cryptobox / owner_connectors | 本页 §1 | ✓ 机制,☐ 字段列表 |
| CalendarProxy / MailProxy + DTO | 本页 §2,connector-plugins | ✓ 代码 |
| openapi Runtime / AuthInjector / StatusError | 本页 §2 | ✓ 代码 |
| DepProvider / DepRegistry / RequiresDeps | connector-deps | ✓ 代码 |
| 摄取(spec 解析、binding 校验) | ☐ 尚未图示 | — |
| egress guard 类型 | connector-egress-guard(散文形式) | ☐ 无类图 |