package service import ( "context" "fmt" "time" "git.zkcoi.com/zkcoi/meshray/internal/dnsprovider" "git.zkcoi.com/zkcoi/meshray/internal/model" "github.com/libdns/libdns" "go.uber.org/zap" "gorm.io/gorm" ) // DDNSOperationService DDNS 操作服务(全功能模式) type DDNSOperationService struct { logger *zap.Logger db *gorm.DB } // NewDDNSOperationService 创建 DDNS 操作服务 func NewDDNSOperationService(logger *zap.Logger, db *gorm.DB) *DDNSOperationService { return &DDNSOperationService{ logger: logger, db: db, } } // CreateDNSRecord 创建 DNS 记录(全功能模式) func (s *DDNSOperationService) CreateDNSRecord(config *model.Service, recordType string, name string, value string, ttl int) error { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() // 1. 获取关联的 DDNS 配置 ddnsConfig, err := s.getDDNSConfig(config.DDNSConfigID) if err != nil { return fmt.Errorf("获取 DDNS 配置失败:%w", err) } // 2. 创建 DNS Provider providerConfig := dnsprovider.ProviderConfig{ Provider: dnsprovider.ProviderType(ddnsConfig.Provider), Domain: ddnsConfig.Domain, APIToken: ddnsConfig.Token, AccessKeyID: ddnsConfig.AuthUsername, AccessKeySecret: ddnsConfig.AuthPassword, SecretId: ddnsConfig.AuthUsername, SecretKey: ddnsConfig.AuthPassword, } provider, err := dnsprovider.NewDNSProvider(providerConfig) if err != nil { return fmt.Errorf("创建 DNS Provider 失败:%w", err) } // 3. 构建 DNS 记录 dnsRecord := &dnsprovider.DNSRecord{ Type: dnsprovider.RecordType(recordType), Name: name, Value: value, TTL: ttl, } // 4. 添加 DNS 记录 zone := ddnsConfig.Domain libdnsRecord := dnsRecord.ToLibdnsRecord() s.logger.Info("开始创建 DNS 记录", zap.String("type", recordType), zap.String("name", name), zap.String("value", value), zap.String("domain", zone)) _, err = provider.AppendRecords(ctx, zone, []libdns.Record{libdnsRecord}) if err != nil { return fmt.Errorf("添加 DNS 记录失败:%w", err) } s.logger.Info("DNS 记录创建成功", zap.String("type", recordType), zap.String("name", name), zap.String("domain", zone)) return nil } // UpdateDNSRecord 更新 DNS 记录(全功能模式) func (s *DDNSOperationService) UpdateDNSRecord(config *model.Service, recordType string, name string, value string, ttl int) error { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() // 1. 获取关联的 DDNS 配置 ddnsConfig, err := s.getDDNSConfig(config.DDNSConfigID) if err != nil { return fmt.Errorf("获取 DDNS 配置失败:%w", err) } // 2. 创建 DNS Provider providerConfig := dnsprovider.ProviderConfig{ Provider: dnsprovider.ProviderType(ddnsConfig.Provider), Domain: ddnsConfig.Domain, APIToken: ddnsConfig.Token, AccessKeyID: ddnsConfig.AuthUsername, AccessKeySecret: ddnsConfig.AuthPassword, SecretId: ddnsConfig.AuthUsername, SecretKey: ddnsConfig.AuthPassword, } provider, err := dnsprovider.NewDNSProvider(providerConfig) if err != nil { return fmt.Errorf("创建 DNS Provider 失败:%w", err) } // 3. 构建新的 DNS 记录 newRecord := &dnsprovider.DNSRecord{ Type: dnsprovider.RecordType(recordType), Name: name, Value: value, TTL: ttl, } // 4. 使用 SetRecords 覆盖现有记录(会自动删除旧记录并创建新记录) zone := ddnsConfig.Domain libdnsRecord := newRecord.ToLibdnsRecord() s.logger.Info("开始更新 DNS 记录", zap.String("type", recordType), zap.String("name", name), zap.String("value", value), zap.String("domain", zone)) _, err = provider.SetRecords(ctx, zone, []libdns.Record{libdnsRecord}) if err != nil { return fmt.Errorf("更新 DNS 记录失败:%w", err) } s.logger.Info("DNS 记录更新成功", zap.String("type", recordType), zap.String("name", name), zap.String("domain", zone)) return nil } // DeleteDNSRecord 删除 DNS 记录(全功能模式) func (s *DDNSOperationService) DeleteDNSRecord(config *model.Service, recordType string, name string) error { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() // 1. 获取关联的 DDNS 配置 ddnsConfig, err := s.getDDNSConfig(config.DDNSConfigID) if err != nil { return fmt.Errorf("获取 DDNS 配置失败:%w", err) } // 2. 创建 DNS Provider providerConfig := dnsprovider.ProviderConfig{ Provider: dnsprovider.ProviderType(ddnsConfig.Provider), Domain: ddnsConfig.Domain, APIToken: ddnsConfig.Token, AccessKeyID: ddnsConfig.AuthUsername, AccessKeySecret: ddnsConfig.AuthPassword, SecretId: ddnsConfig.AuthUsername, SecretKey: ddnsConfig.AuthPassword, } provider, err := dnsprovider.NewDNSProvider(providerConfig) if err != nil { return fmt.Errorf("创建 DNS Provider 失败:%w", err) } // 3. 先获取所有记录 records, err := provider.GetRecords(ctx, ddnsConfig.Domain) if err != nil { return fmt.Errorf("获取 DNS 记录失败:%w", err) } // 4. 找到要删除的记录 var targetRecord *libdns.Record for _, rec := range records { if rec.Type == recordType && rec.Name == name { targetRecord = &rec break } } if targetRecord == nil { s.logger.Warn("DNS 记录不存在,跳过删除", zap.String("type", recordType), zap.String("name", name)) return nil } // 5. 删除记录 s.logger.Info("开始删除 DNS 记录", zap.String("type", recordType), zap.String("name", name), zap.String("domain", ddnsConfig.Domain)) _, err = provider.DeleteRecords(ctx, ddnsConfig.Domain, []libdns.Record{*targetRecord}) if err != nil { return fmt.Errorf("删除 DNS 记录失败:%w", err) } s.logger.Info("DNS 记录删除成功", zap.String("type", recordType), zap.String("name", name), zap.String("domain", ddnsConfig.Domain)) return nil } // getDDNSConfig 获取关联的 DDNS 配置 func (s *DDNSOperationService) getDDNSConfig(configID string) (*model.Service, error) { // 从 Service 表中查询 ID=configID 且 Type=DDNS 的记录 var ddnsService model.Service if err := s.db.Where("id = ? AND type = 'DDNS'", configID).First(&ddnsService).Error; err != nil { return nil, fmt.Errorf("查询 DDNS 配置失败:%w", err) } return &ddnsService, nil } // SyncMeshSeedToDNS 同步 MeshSeed 到 DNS TXT 记录(带重试机制) func (s *DDNSOperationService) SyncMeshSeedToDNS(networkID uint64, seedString string, ddnsServiceID string) error { const maxRetries = 3 var lastErr error // 查询 DDNS 配置 var ddnsService model.Service if err := s.db.First(&ddnsService, ddnsServiceID).Error; err != nil { return fmt.Errorf("查询 DDNS 服务失败:%w", err) } // 查询网络获取前缀 var network model.Network if err := s.db.First(&network, networkID).Error; err != nil { return fmt.Errorf("查询网络失败:%w", err) } // 重试逻辑(指数退避) for attempt := 1; attempt <= maxRetries; attempt++ { lastErr = s.doSyncMeshSeedToDNS(&network, &ddnsService, seedString) if lastErr == nil { // 成功,更新状态 s.updateSyncStatus(networkID, "success", "") s.logger.Info("MeshSeed 同步到 DNS 成功", zap.Uint64("network_id", networkID), zap.Int("attempt", attempt)) return nil } // 失败,记录日志 s.logger.Warn("MeshSeed 同步失败", zap.Uint64("network_id", networkID), zap.Int("attempt", attempt), zap.Error(lastErr)) // 等待后重试(指数退避:1s, 2s, 4s...) if attempt < maxRetries { waitTime := time.Duration(1<