mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 22:06:38 +08:00
314 lines
9.3 KiB
Go
314 lines
9.3 KiB
Go
// Copyright 2026 Arctel.net
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package model
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
|
|
pkgpush "Wavelet/plugins/domain/message_gateway/push"
|
|
)
|
|
|
|
// Push channel and payload constants.
|
|
const (
|
|
ChannelCustom = "custom"
|
|
ChannelEmail = "email"
|
|
ChannelLark = "lark"
|
|
ChannelTelegram = "telegram"
|
|
DefaultLevelInfo = "INFO"
|
|
KeyTitle = "title"
|
|
KeyContent = "content"
|
|
KeyLevel = "level"
|
|
|
|
// KeyURL represents the URL field key.
|
|
KeyURL = "url"
|
|
// KeyToken represents the Token field key.
|
|
KeyToken = "token"
|
|
// KeyOther represents the Other field key.
|
|
KeyOther = "other"
|
|
|
|
// TypeText represents standard text input type.
|
|
TypeText = "text"
|
|
// TypePassword represents password input type.
|
|
TypePassword = "password"
|
|
// TypeTextarea represents textarea input type.
|
|
TypeTextarea = "textarea"
|
|
)
|
|
|
|
// SMTPConfig mirrors the system SMTP settings consumed by the push service.
|
|
type SMTPConfig struct {
|
|
Host string
|
|
Port string
|
|
Username string
|
|
Password string
|
|
}
|
|
|
|
// PushField represents a form field configuration for a channel.
|
|
type PushField struct {
|
|
Key string `json:"key"`
|
|
Label string `json:"label"`
|
|
Type string `json:"type"`
|
|
Required bool `json:"required"`
|
|
Placeholder string `json:"placeholder"`
|
|
Description string `json:"description"`
|
|
}
|
|
|
|
// PushDefinition represents the metadata and form schema for a notification channel.
|
|
type PushDefinition struct {
|
|
Type string `json:"type"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Fields []PushField `json:"fields"`
|
|
}
|
|
|
|
// CreatePushChannelRequest is the create channel request payload.
|
|
type CreatePushChannelRequest struct {
|
|
Name string `json:"name" binding:"required"`
|
|
Description string `json:"description"`
|
|
Type string `json:"type" binding:"required"`
|
|
Token string `json:"token"`
|
|
URL string `json:"url"`
|
|
Other string `json:"other"`
|
|
Enabled bool `json:"enabled"`
|
|
}
|
|
|
|
// UpdatePushChannelRequest is the update channel request payload.
|
|
type UpdatePushChannelRequest struct {
|
|
Description string `json:"description"`
|
|
Type string `json:"type" binding:"required"`
|
|
Token string `json:"token"`
|
|
URL string `json:"url"`
|
|
Other string `json:"other"`
|
|
Enabled bool `json:"enabled"`
|
|
}
|
|
|
|
// TestPushChannelRequest is the test channel request payload.
|
|
type TestPushChannelRequest struct {
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
Token string `json:"token"`
|
|
URL string `json:"url"`
|
|
Other string `json:"other"`
|
|
Target string `json:"target"`
|
|
}
|
|
|
|
// CustomPushRequest contains custom webhook parameters.
|
|
type CustomPushRequest struct {
|
|
Title string `json:"title" form:"title"`
|
|
Description string `json:"description" form:"description"`
|
|
Content string `json:"content" form:"content"`
|
|
URL string `json:"url" form:"url"`
|
|
To string `json:"to" form:"to"`
|
|
Token string `json:"token" form:"token"`
|
|
}
|
|
|
|
// CreatePushEventRequest is the request body for creating a push event.
|
|
type CreatePushEventRequest struct {
|
|
EventKey string `json:"event_key"`
|
|
TaskType string `json:"task_type"`
|
|
Channels []string `json:"channels"`
|
|
Targets []string `json:"targets"`
|
|
Template string `json:"template"`
|
|
Enabled bool `json:"enabled"`
|
|
}
|
|
|
|
// UpdatePushEventRequest is the request body for updating a push event.
|
|
type UpdatePushEventRequest struct {
|
|
Channels []string `json:"channels"`
|
|
Targets []string `json:"targets"`
|
|
Template string `json:"template" binding:"required"`
|
|
Enabled bool `json:"enabled"`
|
|
}
|
|
|
|
// TestPushRequest is the request body for testing push config.
|
|
type TestPushRequest struct {
|
|
Config pkgpush.Config `json:"config" binding:"required"`
|
|
Target string `json:"target"`
|
|
}
|
|
|
|
// NotificationMessage represents the structured notification message payload.
|
|
type NotificationMessage struct {
|
|
Title string `json:"title"`
|
|
Content string `json:"content"`
|
|
Level string `json:"level"`
|
|
Ext map[string]any `json:"ext,omitempty"`
|
|
}
|
|
|
|
// Flatten converts the structured NotificationMessage back to a flat map (original json structure).
|
|
func (m NotificationMessage) Flatten() map[string]any {
|
|
res := map[string]any{
|
|
KeyTitle: m.Title,
|
|
KeyContent: m.Content,
|
|
KeyLevel: m.Level,
|
|
}
|
|
for k, v := range m.Ext {
|
|
res[k] = v
|
|
}
|
|
return res
|
|
}
|
|
|
|
// EventMetadata represents the metadata of a push notification event.
|
|
type EventMetadata struct {
|
|
Key string `json:"key"`
|
|
Name string `json:"name"`
|
|
DefaultTemplate NotificationMessage `json:"default_template"`
|
|
Description string `json:"description"`
|
|
}
|
|
|
|
// SendPayload is the async push dispatch载荷 consumed by the notification worker.
|
|
type SendPayload struct {
|
|
EventKey string `json:"event_key"`
|
|
Config pkgpush.Config `json:"config"`
|
|
Target string `json:"target"`
|
|
Body NotificationMessage `json:"body"`
|
|
Template string `json:"template"`
|
|
}
|
|
|
|
// PushHistoryListFilter filters push history pagination queries.
|
|
type PushHistoryListFilter struct {
|
|
EventKey string
|
|
Channel string
|
|
Status string
|
|
StartTime *time.Time
|
|
EndTime *time.Time
|
|
Page int
|
|
PageSize int
|
|
}
|
|
|
|
var (
|
|
pushDefMu sync.RWMutex
|
|
pushDefinitions = make(map[string]PushDefinition)
|
|
)
|
|
|
|
// RegisterPushChannelDefinition registers a channel definition.
|
|
func RegisterPushChannelDefinition(def PushDefinition) {
|
|
pushDefMu.Lock()
|
|
defer pushDefMu.Unlock()
|
|
pushDefinitions[def.Type] = def
|
|
}
|
|
|
|
// ListPushDefinitions returns all registered channel definitions.
|
|
func ListPushDefinitions() []PushDefinition {
|
|
pushDefMu.RLock()
|
|
defer pushDefMu.RUnlock()
|
|
|
|
order := []string{ChannelCustom, ChannelLark, ChannelTelegram, ChannelEmail}
|
|
res := make([]PushDefinition, 0, len(pushDefinitions))
|
|
for _, t := range order {
|
|
if d, ok := pushDefinitions[t]; ok {
|
|
res = append(res, d)
|
|
}
|
|
}
|
|
for t, d := range pushDefinitions {
|
|
found := false
|
|
for _, o := range order {
|
|
if o == t {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
res = append(res, d)
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
func init() {
|
|
RegisterPushChannelDefinition(PushDefinition{
|
|
Type: ChannelCustom,
|
|
Name: "自定义消息通道",
|
|
Description: "使用自定义 HTTP POST 请求向外部 Webhook 发送数据。",
|
|
Fields: []PushField{
|
|
{
|
|
Key: KeyURL,
|
|
Label: "请求地址",
|
|
Type: TypeText,
|
|
Required: true,
|
|
Placeholder: "在此填写完整的请求地址,必须使用 HTTPS 协议",
|
|
Description: "接口请求的完整 HTTPS URL,例如 https://api.example.com/webhook",
|
|
},
|
|
{
|
|
Key: KeyOther,
|
|
Label: "请求体 (JSON)",
|
|
Type: TypeTextarea,
|
|
Required: true,
|
|
Placeholder: "在此输入请求体,支持模板变量,必须为合法的 JSON 格式",
|
|
Description: "可使用的变量:$title, $description, $content, $url, $to。例如 {\"text\": \"$content\"}",
|
|
},
|
|
},
|
|
})
|
|
|
|
RegisterPushChannelDefinition(PushDefinition{
|
|
Type: ChannelLark,
|
|
Name: "飞书群机器人",
|
|
Description: "配置飞书群自定义机器人的 Webhook 接口投递。",
|
|
Fields: []PushField{
|
|
{
|
|
Key: KeyURL,
|
|
Label: "Webhook 地址",
|
|
Type: TypeText,
|
|
Required: true,
|
|
Placeholder: "https://open.feishu.cn/open-apis/bot/v2/hook/YOUR_TOKEN",
|
|
Description: "从飞书群机器人设置中复制的 Webhook URL",
|
|
},
|
|
{
|
|
Key: KeyToken,
|
|
Label: "签名校验密钥 (Secret) (可选)",
|
|
Type: TypeText,
|
|
Required: false,
|
|
Placeholder: "可选,若机器人启用了安全设置中的签名校验,请在此输入",
|
|
Description: "飞书群机器人安全设置中的签名校验 Key",
|
|
},
|
|
{
|
|
Key: KeyOther,
|
|
Label: "自定义卡片 JSON 模版 (可选)",
|
|
Type: TypeTextarea,
|
|
Required: false,
|
|
Placeholder: "可选,留空则默认使用系统内置的精美互动卡片",
|
|
Description: "若填写,必须是合法的飞书卡片 JSON 格式",
|
|
},
|
|
},
|
|
})
|
|
|
|
RegisterPushChannelDefinition(PushDefinition{
|
|
Type: ChannelTelegram,
|
|
Name: "Telegram 机器人",
|
|
Description: "配置 Telegram 机器人推送消息。",
|
|
Fields: []PushField{
|
|
{
|
|
Key: KeyURL,
|
|
Label: "API 基础地址 (可选)",
|
|
Type: TypeText,
|
|
Required: false,
|
|
Placeholder: "https://api.telegram.org",
|
|
Description: "接口请求的 HTTPS 基础地址,留空默认为 https://api.telegram.org",
|
|
},
|
|
{
|
|
Key: KeyToken,
|
|
Label: "机器人 Token (Bot Token)",
|
|
Type: TypePassword,
|
|
Required: true,
|
|
Placeholder: "在此输入 Telegram 机器人的 Bot Token",
|
|
Description: "通过 BotFather 申请到的机器人 Access Token",
|
|
},
|
|
{
|
|
Key: KeyOther,
|
|
Label: "默认会话 ID (Chat ID) (可选)",
|
|
Type: TypeText,
|
|
Required: false,
|
|
Placeholder: "例如 -100123456789 或 @channel_name",
|
|
Description: "默认的消息接收 Chat ID。如果通知事件中未配置 targets,将推送到此 ID",
|
|
},
|
|
},
|
|
})
|
|
|
|
RegisterPushChannelDefinition(PushDefinition{
|
|
Type: ChannelEmail,
|
|
Name: "邮件推送通道",
|
|
Description: "邮件推送通道直接使用系统全局 SMTP 设置进行发送,无需在此填写服务器配置。",
|
|
Fields: []PushField{},
|
|
})
|
|
}
|