feat: custom domain management, config and notification fixes, and VPS auto-renewal rollover

This commit is contained in:
Bot
2026-09-01 23:45:21 +08:00
parent 00f5777112
commit 3d2b27f4ec
30 changed files with 610 additions and 359 deletions
+19 -18
View File
@@ -35,14 +35,14 @@ const (
)
type ConfigForGuests struct {
Language string `koanf:"language" json:"language"` // 系统语言,默认 zh_CN
SiteName string `koanf:"site_name" json:"site_name"`
CustomCode string `koanf:"custom_code" json:"custom_code,omitempty"`
CustomCodeDashboard string `koanf:"custom_code_dashboard" json:"custom_code_dashboard,omitempty"`
CustomLogo string `koanf:"custom_logo" json:"custom_logo,omitempty"`
CustomDescription string `koanf:"custom_description" json:"custom_description,omitempty"`
CustomLinks string `koanf:"custom_links" json:"custom_links,omitempty"`
BackgroundImageDay string `koanf:"background_image_day" json:"background_image_day,omitempty"`
Language string `koanf:"language" json:"language"` // 系统语言,默认 zh_CN
SiteName string `koanf:"site_name" json:"site_name"`
CustomCode string `koanf:"custom_code" json:"custom_code,omitempty"`
CustomCodeDashboard string `koanf:"custom_code_dashboard" json:"custom_code_dashboard,omitempty"`
CustomLogo string `koanf:"custom_logo" json:"custom_logo,omitempty"`
CustomDescription string `koanf:"custom_description" json:"custom_description,omitempty"`
CustomLinks string `koanf:"custom_links" json:"custom_links,omitempty"`
BackgroundImageDay string `koanf:"background_image_day" json:"background_image_day,omitempty"`
BackgroundImageNight string `koanf:"background_image_night" json:"background_image_night,omitempty"`
}
@@ -65,7 +65,7 @@ type ConfigDashboard struct {
UserTemplate string `koanf:"user_template" json:"user_template,omitempty"`
AdminTemplate string `koanf:"admin_template" json:"admin_template,omitempty"`
EnablePlainIPInNotification bool `koanf:"enable_plain_ip_in_notification" json:"enable_plain_ip_in_notification,omitempty"` // 通知信息IP不打码
EnablePlainIPInNotification bool `koanf:"enable_plain_ip_in_notification" json:"enable_plain_ip_in_notification,omitempty"` // 通知信息IP不打码
EnableMCP bool `koanf:"enable_mcp" json:"enable_mcp,omitempty"` // 是否启用 MCP 入口(默认关闭;启用前请审视 PAT scope/whitelist
@@ -80,15 +80,15 @@ type ConfigDashboard struct {
Cover uint8 `koanf:"cover" json:"cover"` // 覆盖范围(0:提醒未被 IgnoredIPNotification 包含的所有服务器; 1:仅提醒被 IgnoredIPNotification 包含的服务器;
IgnoredIPNotification string `koanf:"ignored_ip_notification" json:"ignored_ip_notification,omitempty"` // 特定服务器IP(多个服务器用逗号分隔)
DNSServers string `koanf:"dns_servers" json:"dns_servers,omitempty"`
ExpiryNotificationGroupID uint64 `koanf:"expiry_notification_group_id" json:"expiry_notification_group_id,omitempty"`
TelegramBotToken string `koanf:"telegram_bot_token" json:"telegram_bot_token,omitempty"`
TelegramAdminChatID string `koanf:"telegram_admin_chat_id" json:"telegram_admin_chat_id,omitempty"`
DNSServers string `koanf:"dns_servers" json:"dns_servers,omitempty"`
ExpiryNotificationGroupID uint64 `koanf:"expiry_notification_group_id" json:"expiry_notification_group_id,omitempty"`
TelegramBotToken string `koanf:"telegram_bot_token" json:"telegram_bot_token,omitempty"`
TelegramAdminChatID string `koanf:"telegram_admin_chat_id" json:"telegram_admin_chat_id,omitempty"`
SMTPServer string `koanf:"smtp_server" json:"smtp_server,omitempty"`
SMTPUser string `koanf:"smtp_user" json:"smtp_user,omitempty"`
SMTPPassword string `koanf:"smtp_password" json:"smtp_password,omitempty"`
AdminEmail string `koanf:"admin_email" json:"admin_email,omitempty"`
SMTPServer string `koanf:"smtp_server" json:"smtp_server,omitempty"`
SMTPUser string `koanf:"smtp_user" json:"smtp_user,omitempty"`
SMTPPassword string `koanf:"smtp_password" json:"smtp_password,omitempty"`
AdminEmail string `koanf:"admin_email" json:"admin_email,omitempty"`
DomainExpiryNotificationDays string `koanf:"domain_expiry_notification_days" json:"domain_expiry_notification_days,omitempty"`
ServerExpiryNotificationDays string `koanf:"server_expiry_notification_days" json:"server_expiry_notification_days,omitempty"`
}
@@ -397,8 +397,9 @@ func koanfConf(c any) koanf.UnmarshalConf {
WeaklyTypedInput: true,
MatchName: func(mapKey, fieldName string) bool {
return strings.EqualFold(mapKey, fieldName) ||
strings.EqualFold(mapKey, strings.ReplaceAll(fieldName, "_", ""))
strings.EqualFold(strings.ReplaceAll(mapKey, "_", ""), strings.ReplaceAll(fieldName, "_", ""))
},
Squash: true,
},
}
+27 -1
View File
@@ -36,7 +36,23 @@ func TestReadConfig(t *testing.T) {
})
t.Run("ReadFile", func(t *testing.T) {
const testCfg = "jwt_secret_key: test\nuser_template: um\nadmin_template: am\nagent_secret_key: none\nsite_name: lowkick"
const testCfg = `
jwt_secret_key: test
user_template: um
admin_template: am
agent_secret_key: none
site_name: lowkick
telegram_bot_token: 8155880635:AAH_test
telegram_admin_chat_id: "12345678"
web_real_ip_header: nz-realip
agent_real_ip_header: nz-realip
domain_expiry_notification_days: 100, 30, 7, 1, 0
server_expiry_notification_days: 30, 7, 3, 1, 0
smtp_server: us1.workspace.org:465
smtp_user: test@loohui.com
smtp_password: password123
admin_email: admin@loohui.com
`
var testFrontendTemplates = []FrontendTemplate{
{Path: "um"},
@@ -59,6 +75,16 @@ func TestReadConfig(t *testing.T) {
{"admin_template", c.AdminTemplate, c.AdminTemplate == "am"},
{"agent_secret_key", c.AgentSecretKey, c.AgentSecretKey == "none"},
{"site_name", c.SiteName, c.SiteName == "lowkick"},
{"telegram_bot_token", c.TelegramBotToken, c.TelegramBotToken == "8155880635:AAH_test"},
{"telegram_admin_chat_id", c.TelegramAdminChatID, c.TelegramAdminChatID == "12345678"},
{"web_real_ip_header", c.WebRealIPHeader, c.WebRealIPHeader == "nz-realip"},
{"agent_real_ip_header", c.AgentRealIPHeader, c.AgentRealIPHeader == "nz-realip"},
{"domain_expiry_notification_days", c.DomainExpiryNotificationDays, c.DomainExpiryNotificationDays == "100, 30, 7, 1, 0"},
{"server_expiry_notification_days", c.ServerExpiryNotificationDays, c.ServerExpiryNotificationDays == "30, 7, 3, 1, 0"},
{"smtp_server", c.SMTPServer, c.SMTPServer == "us1.workspace.org:465"},
{"smtp_user", c.SMTPUser, c.SMTPUser == "test@loohui.com"},
{"smtp_password", c.SMTPPassword, c.SMTPPassword == "password123"},
{"admin_email", c.AdminEmail, c.AdminEmail == "admin@loohui.com"},
}
for _, field := range testFields {
-28
View File
@@ -143,7 +143,6 @@ func (ns *NotificationServerBundle) Send(message string) error {
verifyTLS := n.VerifyTLS != nil && *n.VerifyTLS
reqBody, err := ns.reqBody(message)
if err != nil {
return err
@@ -331,33 +330,6 @@ func (ns *NotificationServerBundle) replaceParamsInString(str string, message st
)
}
replacer := strings.NewReplacer(replacements...)
return replacer.Replace(str)
}
var ipv4, ipv6, validIP string
if ns.Server.GeoIP != nil {
ip := ns.Server.GeoIP.IP
if ip.IPv4Addr != "" && ip.IPv6Addr != "" {
ipv4 = ip.IPv4Addr
ipv6 = ip.IPv6Addr
validIP = ipv4
} else if ip.IPv4Addr != "" {
ipv4 = ip.IPv4Addr
validIP = ipv4
} else {
ipv6 = ip.IPv6Addr
validIP = ipv6
}
}
replacements = append(replacements,
"#SERVER.IP#", mod(validIP),
"#SERVER.IPV4#", mod(ipv4),
"#SERVER.IPV6#", mod(ipv6),
)
}
replacer := strings.NewReplacer(replacements...)
return replacer.Replace(str)
}
+9 -10
View File
@@ -10,7 +10,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/goccy/go-json"
"gorm.io/datatypes"
"gorm.io/gorm"
pb "github.com/nezhahq/nezha/proto"
@@ -21,15 +20,15 @@ var runtimeHolderInitMu sync.Mutex
type Server struct {
Common
Name string `json:"name"`
UUID string `json:"uuid,omitempty" gorm:"unique"`
Note string `json:"note,omitempty"` // 管理员可见备注
PublicNote string `json:"public_note,omitempty"` // 公开备注
DisplayIndex int `json:"display_index"` // 展示排序,越大越靠前
HideForGuest bool `json:"hide_for_guest,omitempty"` // 对游客隐藏
EnableDDNS bool `json:"enable_ddns,omitempty"` // 启用DDNS
BillingData datatypes.JSON `gorm:"type:json" json:"billing_data,omitempty"`
DDNSProfilesRaw string `gorm:"default:'[]';column:ddns_profiles_raw" json:"-"`
Name string `json:"name"`
UUID string `json:"uuid,omitempty" gorm:"unique"`
Note string `json:"note,omitempty"` // 管理员可见备注
PublicNote string `json:"public_note,omitempty"` // 公开备注
DisplayIndex int `json:"display_index"` // 展示排序,越大越靠前
HideForGuest bool `json:"hide_for_guest,omitempty"` // 对游客隐藏
EnableDDNS bool `json:"enable_ddns,omitempty"` // 启用DDNS
DDNSProfilesRaw string `gorm:"default:'[]';column:ddns_profiles_raw" json:"-"`
OverrideDDNSDomainsRaw string `gorm:"default:'{}';column:override_ddns_domains_raw" json:"-"`
DDNSProfiles []uint64 `gorm:"-" json:"ddns_profiles,omitempty" validate:"optional"` // DDNS配置
+7 -8
View File
@@ -21,13 +21,13 @@ type SettingForm struct {
BackgroundImageDay string `json:"background_image_day,omitempty" validate:"optional"`
BackgroundImageNight string `json:"background_image_night,omitempty" validate:"optional"`
AgentTLS bool `json:"tls,omitempty" validate:"optional"`
EnableIPChangeNotification bool `json:"enable_ip_change_notification,omitempty" validate:"optional"`
EnablePlainIPInNotification bool `json:"enable_plain_ip_in_notification,omitempty" validate:"optional"`
EnableMCP *bool `json:"enable_mcp,omitempty" validate:"optional"`
ExpiryNotificationGroupID uint64 `json:"expiry_notification_group_id,omitempty"`
TelegramBotToken string `json:"telegram_bot_token,omitempty" validate:"optional"`
TelegramAdminChatID string `json:"telegram_admin_chat_id,omitempty" validate:"optional"`
AgentTLS bool `json:"tls,omitempty" validate:"optional"`
EnableIPChangeNotification bool `json:"enable_ip_change_notification,omitempty" validate:"optional"`
EnablePlainIPInNotification bool `json:"enable_plain_ip_in_notification,omitempty" validate:"optional"`
EnableMCP *bool `json:"enable_mcp,omitempty" validate:"optional"`
ExpiryNotificationGroupID uint64 `json:"expiry_notification_group_id,omitempty"`
TelegramBotToken string `json:"telegram_bot_token,omitempty" validate:"optional"`
TelegramAdminChatID string `json:"telegram_admin_chat_id,omitempty" validate:"optional"`
SMTPServer string `json:"smtp_server,omitempty" validate:"optional"`
SMTPUser string `json:"smtp_user,omitempty" validate:"optional"`
@@ -35,7 +35,6 @@ type SettingForm struct {
AdminEmail string `json:"admin_email,omitempty" validate:"optional"`
DomainExpiryNotificationDays string `json:"domain_expiry_notification_days,omitempty" validate:"optional"`
ServerExpiryNotificationDays string `json:"server_expiry_notification_days,omitempty" validate:"optional"`
}
type Setting struct {