feat(gateway): webhook 渠道 InboundHandler 挂载到网关(渠道遗留2)
让 webhook 渠道的入站 HTTP 回调真正进运行时(此前 handler 建了但未挂载,回调不达 runtime)。 - channelbridge:Bridge.WebhookHandlers() map[string]http.Handler——遍历适配器,对暴露 InboundHandler()/Config() 的(*webhook.Adapter)收集 endpointPath→handler。 - gateway/server.go:Deps 加 Webhooks map[string]http.Handler;Server 存 webhooks; Handler() 挂载到 mux(无网关鉴权,各 handler 自带 HMAC 签名校验)。 - gateway/static.go:spa 加 isWebhookRoute——已注册的 webhook 路径交给 API mux(否则被 SPA fallback 吞成 index.html,webhook 回调不达)。 - wire.go:gateway.Deps 传 Webhooks: a.channelBridge.WebhookHandlers()(wireChannels 在 wireFrontend 前执行,bridge 已建好)。 实机验证:POST /webhook 空体→bad_request(400,webhook 解码响应非 SPA)、带 JSON→accepted(200)、 GET /webhook 被 webhook 路由处理。go build/vet/test 全绿。
This commit is contained in:
1 parent
adad4e1f62
commit
76134ed47e
4 files changed
+70
-1
No files matched your search
@@ -16,6 +16,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -28,6 +29,18 @@ import (
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/user"
|
||||
)
|
||||
|
||||
// webhookAdapter is the optional capability a channel adapter may expose to
|
||||
// source an HTTP inbound callback. *webhook.Adapter implements it via
|
||||
// InboundHandler(). The bridge reports these so the gateway can mount them.
|
||||
type webhookAdapter interface {
|
||||
// InboundHandler returns the http.Handler the gateway mounts at the adapter's
|
||||
// configured endpoint path to receive external callbacks.
|
||||
InboundHandler() http.Handler
|
||||
// Config returns the adapter config snapshot; the endpoint path is read from
|
||||
// it so the gateway can register the correct route.
|
||||
Config() map[string]any
|
||||
}
|
||||
|
||||
// Knob constants. Compile-time so no bare magic number appears in the run path
|
||||
// (project rule §10).
|
||||
const (
|
||||
@@ -123,6 +136,33 @@ func (b *Bridge) Add(a adapter.PlatformAdapter) {
|
||||
b.adapters[a.Name()] = a
|
||||
}
|
||||
|
||||
// WebhookHandlers returns the inbound HTTP handlers of every registered adapter
|
||||
// that exposes an HTTP callback (e.g. *webhook.Adapter), keyed by the endpoint
|
||||
// path from the adapter's config. The gateway mounts these to receive external
|
||||
// webhook callbacks into the runtime. A nil map is returned when no adapter
|
||||
// provides an inbound handler.
|
||||
func (b *Bridge) WebhookHandlers() map[string]http.Handler {
|
||||
b.mu.RLock()
|
||||
defer b.mu.RUnlock()
|
||||
out := make(map[string]http.Handler)
|
||||
for _, a := range b.adapters {
|
||||
wh, ok := a.(webhookAdapter)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
cfg := wh.Config()
|
||||
path, _ := cfg["endpointPath"].(string)
|
||||
if path == "" {
|
||||
path = "/webhook"
|
||||
}
|
||||
out[path] = wh.InboundHandler()
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return nil
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Start connects every configured adapter, installs the inbound handler, and
|
||||
// launches the read loops. Adapters without credentials (or whose Connect fails)
|
||||
// are logged and left disconnected; they never fail startup. Start blocks only
|
||||
|
||||
Reference in new issue
Block a user