mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 22:06:38 +08:00
63cd906cfc
- Merge all files inside openflare-server to the repository root directory. - Relocate agent, relay, and flared subprojects from internal/ to internal/apps/. - Combine docker-compose files and update build context paths to root. - Update GitHub workflows and Dockerfiles to refer to new directories and package names. - Rewrite Go package imports across all files. - Resolve database renew test race condition and clean up docs.
100 lines
3.1 KiB
Go
100 lines
3.1 KiB
Go
// Copyright 2026 Arctel.net
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package model
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
// TypeCustom 自定义消息通道类型
|
|
TypeCustom = "custom"
|
|
// TypeEmail 邮件推送消息通道类型
|
|
TypeEmail = "email"
|
|
// TypeTelegram 电报机器人推送消息通道类型
|
|
TypeTelegram = "telegram"
|
|
)
|
|
|
|
// PushChannel 消息通道模型
|
|
type PushChannel struct {
|
|
ID uint64 `json:"id" gorm:"primaryKey;autoIncrement"`
|
|
Name string `json:"name" gorm:"uniqueIndex;size:80;not null"` // 通道名称,仅英文字母和下划线,唯一
|
|
Description string `json:"description" gorm:"size:255"` // 备注
|
|
Type string `json:"type" gorm:"size:50;not null;default:'custom'"` // 通道类型:custom, lark, email
|
|
Token string `json:"token" gorm:"size:100"` // 鉴权令牌或发信用户名等
|
|
URL string `json:"url" gorm:"type:text;not null"` // 请求地址,HTTPS 协议或 SMTP 地址
|
|
Other string `json:"other" gorm:"type:text;not null"` // 请求体/SMTP 密码等
|
|
Enabled bool `json:"enabled" gorm:"index;not null;default:true"` // 通道是否启用
|
|
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime;index"`
|
|
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime;index"`
|
|
}
|
|
|
|
// TableName 指定 GORM 表名
|
|
func (PushChannel) TableName() string {
|
|
return "w_push_channels"
|
|
}
|
|
|
|
var nameRegex = regexp.MustCompile(`^[a-zA-Z0-9_]+$`)
|
|
|
|
// Validate 参数合法性与 JSON 格式校验
|
|
func (pc *PushChannel) Validate() error {
|
|
pc.Name = strings.TrimSpace(pc.Name)
|
|
pc.URL = strings.TrimSpace(pc.URL)
|
|
pc.Other = strings.TrimSpace(pc.Other)
|
|
pc.Type = strings.TrimSpace(pc.Type)
|
|
|
|
if pc.Type == "" {
|
|
pc.Type = TypeCustom
|
|
}
|
|
|
|
if pc.Type == TypeTelegram && pc.URL == "" {
|
|
pc.URL = "https://api.telegram.org"
|
|
}
|
|
|
|
if pc.Name == "" {
|
|
return errors.New("channel name is required")
|
|
}
|
|
if !nameRegex.MatchString(pc.Name) {
|
|
return errors.New("channel name can only contain letters, numbers, and underscores")
|
|
}
|
|
if pc.Type != TypeEmail && pc.URL == "" {
|
|
return errors.New("request URL/address is required")
|
|
}
|
|
|
|
if pc.Type != TypeEmail && !strings.HasPrefix(pc.URL, "https://") {
|
|
return errors.New("request URL must use HTTPS protocol for security reasons")
|
|
}
|
|
|
|
switch pc.Type {
|
|
case TypeCustom:
|
|
if pc.Other == "" {
|
|
return errors.New("payload schema (request body) is required")
|
|
}
|
|
return validateJSON(pc.Other)
|
|
case TypeEmail:
|
|
// Email channel SMTP configs fall back to global settings, so they are not required to be filled.
|
|
case TypeTelegram:
|
|
if pc.Token == "" {
|
|
return errors.New("telegram bot token is required")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateJSON(s string) error {
|
|
var jsonTest map[string]any
|
|
if err := json.Unmarshal([]byte(s), &jsonTest); err == nil {
|
|
return nil
|
|
}
|
|
var jsonArr []any
|
|
if err := json.Unmarshal([]byte(s), &jsonArr); err == nil {
|
|
return nil
|
|
}
|
|
return errors.New("payload schema must be a valid JSON format")
|
|
}
|