Files
nezha_domains/service/singleton/domain.go
2025-09-12 15:30:07 +00:00

195 lines
4.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// service/singleton/domain.go
package singleton
import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"log"
"net"
"strings"
"time"
"github.com/nezhahq/nezha/model"
"gorm.io/datatypes"
)
// GetDomains 获取所有域名记录
func GetDomains(scope string) ([]model.Domain, error) {
var domains []model.Domain
query := DB
if scope == "public" {
// 如果是公开访问,只返回已验证且公开的域名
query = query.Where("status IN (?, ?) AND is_public = ?", "verified", "expired", true)
}
if err := query.Find(&domains).Error; err != nil {
return nil, err
}
return domains, nil
}
// GetDomainByID 根据ID获取单个域名记录
func GetDomainByID(id uint64) (*model.Domain, error) {
var domain model.Domain
if err := DB.First(&domain, id).Error; err != nil {
return nil, err
}
return &domain, nil
}
// AddDomain 添加一个新的域名并自动生成验证Token
func AddDomain(domainName string) (*model.Domain, error) {
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil {
return nil, fmt.Errorf("无法生成随机Token: %w", err)
}
token := "nezha-verify-" + hex.EncodeToString(b)
newDomain := &model.Domain{
Domain: strings.ToLower(domainName),
VerifyToken: token,
Status: "pending",
}
if err := DB.Create(newDomain).Error; err != nil {
return nil, err
}
return newDomain, nil
}
// VerifyDomain 验证域名的 TXT 记录是否正确
func VerifyDomain(id uint64) (bool, error) {
domain, err := GetDomainByID(id) // 直接调用 GetDomainByID
if err != nil {
return false, err
}
if domain.Status == "verified" {
return true, nil
}
txtRecords, err := net.LookupTXT(domain.Domain)
if err != nil {
var dnsErr *net.DNSError
if errors.As(err, &dnsErr) && dnsErr.IsNotFound {
return false, nil
}
return false, fmt.Errorf("DNS查询失败: %w", err)
}
for _, record := range txtRecords {
if record == domain.VerifyToken {
domain.Status = "verified"
return true, DB.Save(domain).Error
}
}
return false, nil
}
// UpdateDomainConfig 更新指定域名的配置信息
func UpdateDomainConfig(id uint64, billingData datatypes.JSON) (*model.Domain, error) {
domain, err := GetDomainByID(id) // 直接调用 GetDomainByID
if err != nil {
return nil, err
}
domain.BillingData = billingData
if err := DB.Save(domain).Error; err != nil {
return nil, err
}
return domain, nil
}
// UpdateDomain 更新域名信息 (重命名并增强)
func UpdateDomain(id uint64, req model.DomainUpdateRequest) (*model.Domain, error) { // 使用新的请求体
domain, err := GetDomainByID(id)
if err != nil {
return nil, err
}
domain.IsPublic = req.IsPublic
domain.BillingData = req.BillingData
if err := DB.Save(domain).Error; err != nil {
return nil, err
}
return domain, nil
}
// DeleteDomain 删除一个域名记录
func DeleteDomain(id uint64) error {
return DB.Delete(&model.Domain{}, id).Error
}
// CronJobForDomainStatus 检查域名到期和自动续费的定时任务
func CronJobForDomainStatus() {
log.Println("NEZHA>> Cron::开始执行域名状态检查任务")
var domains []model.Domain
if err := DB.Where("status = ?", "verified").Find(&domains).Error; err != nil {
log.Printf("NEZHA>> Cron::Error fetching domains: %v", err)
return
}
now := time.Now()
for i := range domains {
d := domains[i]
if d.BillingData == nil {
continue
}
var billing model.BillingDataMod
if err := json.Unmarshal(d.BillingData, &billing); err != nil {
log.Printf("NEZHA>> Cron::Error parsing billing data for domain %s: %v", d.Domain, err)
continue
}
if billing.EndDate == "" {
continue
}
endDate, err := time.Parse(time.RFC3339, billing.EndDate)
if err != nil {
log.Printf("NEZHA>> Cron::Error parsing end date for domain %s: %v", d.Domain, err)
continue
}
if now.After(endDate) {
if billing.AutoRenewal == "1" {
var newEndDate time.Time
renewalYears := 0
renewalMonths := 0
switch billing.Cycle {
case "年":
renewalYears = 1
case "月":
renewalMonths = 1
default:
log.Printf("NEZHA>> Cron::未知续费周期 '%s' for domain %s", billing.Cycle, d.Domain)
continue
}
newEndDate = endDate.AddDate(renewalYears, renewalMonths, 0)
billing.EndDate = newEndDate.Format(time.RFC3339)
newBillingData, _ := json.Marshal(billing)
d.BillingData = newBillingData
log.Printf("NEZHA>> Cron::域名 %s 已自动续费至 %s", d.Domain, billing.EndDate)
if err := DB.Save(&d).Error; err != nil {
log.Printf("NEZHA>> Cron::Error saving auto-renewed domain %s: %v", d.Domain, err)
}
} else {
d.Status = "expired"
log.Printf("NEZHA>> Cron::域名 %s 已过期", d.Domain)
if err := DB.Save(&d).Error; err != nil {
log.Printf("NEZHA>> Cron::Error marking domain %s as expired: %v", d.Domain, err)
}
}
}
}
log.Println("NEZHA>> Cron::域名状态检查任务执行完毕")
}