Files
goodBaby/internal/gateway/webhook.go
T
mei 9b656cebee
Quality check / Web UI (push) Successful in 9m19s
feat(gateway): 优化gateway相关功能
2026-09-12 15:32:27 +08:00

108 lines
3.2 KiB
Go

package gateway
import (
"context"
"encoding/json"
"fmt"
"github.com/ssdomei232/goodBaby/handler/runner"
"github.com/ssdomei232/goodBaby/internal/meta"
"github.com/ssdomei232/goodBaby/model"
)
// messageFields 规则配置里可以被投递消息覆盖的字段名
var messageFields = []string{"msg", "message", "body"}
// titleField 规则配置里可以被投递标题覆盖的字段名
const titleField = "title"
// TestMessage 是「测试」网关规则时使用的示例投递内容,
// 让用户在页面上就能看到消息被替换后的效果。
var TestMessage = Message{
Title: "goodBaby 测试标题",
Content: "这是一条来自消息网关的测试消息",
}
// WebhookGateway 默认网关:外部系统 POST 一条消息来触发规则
type WebhookGateway struct{}
func (g *WebhookGateway) GetType() string {
return model.GatewayTypeWebhook
}
func (g *WebhookGateway) Meta() meta.GatewayMeta {
return meta.GatewayMeta{
Type: model.GatewayTypeWebhook,
Label: "Webhook",
Description: "为外部系统生成一个 Webhook 地址,POST 一条消息即可触发绑定在该网关上的规则。",
Docs: "docs/gateway-config.md",
PayloadHint: `{"title": "监控告警", "message": "服务已恢复"}`,
}
}
// Deliver 逐条执行绑定在网关上的规则
//
// 单条规则失败不影响其它规则,失败原因汇总在 Result 里返回。
func (g *WebhookGateway) Deliver(ctx context.Context, task *Task) (*Result, error) {
// 初始化为空切片:nil 切片会被序列化成 JSON null,前端读 failed.length 时会报错
failed := []string{}
for i := range task.Rules {
rule := task.Rules[i]
executed := rule
configJSON, applied, err := ApplyMessage(rule.ConfigJson, task.Message)
if err != nil {
failed = append(failed, rule.Name+": "+err.Error())
continue
}
if applied {
// 用投递进来的消息执行,但不改动数据库里的规则配置
executed.ConfigJson = configJSON
}
if err := runner.ExecuteGatewayRuleWithContext(ctx, &executed, model.TriggerGateway); err != nil {
failed = append(failed, rule.Name+": "+err.Error())
}
}
return &Result{Total: len(task.Rules), Failed: failed}, nil
}
// ApplyMessage 把投递进来的消息覆盖到规则配置的消息字段上。
//
// 返回覆盖后的配置;applied 为 false 表示配置里没有可覆盖的消息字段
// (例如「公开 GitHub 仓库」「删除 DNS 记录」),此时按规则里保存的配置执行。
func ApplyMessage(configJSON string, msg Message) (string, bool, error) {
var config map[string]any
if err := json.Unmarshal([]byte(configJSON), &config); err != nil {
return configJSON, false, fmt.Errorf("解析规则配置失败: %v", err)
}
matched := false
for _, key := range messageFields {
if _, ok := config[key]; ok {
if msg.Content != "" {
config[key] = msg.Content
}
matched = true
}
}
if msg.Title != "" {
if _, ok := config[titleField]; ok {
config[titleField] = msg.Title
matched = true
}
}
if !matched {
return configJSON, false, nil
}
out, err := json.Marshal(config)
if err != nil {
return configJSON, false, fmt.Errorf("生成规则配置失败: %v", err)
}
return string(out), true, nil
}