package cmd import ( "agent-wdd/cloudflare" "agent-wdd/log" "bufio" "fmt" "os" "strconv" "strings" "text/tabwriter" "github.com/spf13/cobra" ) const ( // Cloudflare API设置选项 cfAPITokenDefault = "T7LxBemfe8SNGWkT9uz2XIc1e22ifAbBv_POJvDP" // 默认的API令牌,实际应用中应从配置文件或环境变量获取 ) // DNS记录类型常量 const ( DNSTypeA = "A" DNSTypeAAAA = "AAAA" DNSTypeCNAME = "CNAME" DNSTypeTXT = "TXT" DNSTypeMX = "MX" DNSTypeSRV = "SRV" ) // 初始化Cloudflare客户端 func initCloudflareClient() *cloudflare.CloudflareClient { log.Debug("初始化Cloudflare客户端") return cloudflare.GetInstance(cfAPITokenDefault) } // 询问用户确认 func askForConfirmation(prompt string) bool { reader := bufio.NewReader(os.Stdin) for { fmt.Printf("%s [y/n]: ", prompt) response, err := reader.ReadString('\n') if err != nil { log.Error("读取用户输入失败: %v", err) return false } response = strings.ToLower(strings.TrimSpace(response)) if response == "y" || response == "yes" { return true } else if response == "n" || response == "no" { return false } } } // 添加Cloudflare命令 func addCloudflareSubcommands(cmd *cobra.Command) { // 域名管理命令 domainCmd := &cobra.Command{ Use: "domain", Short: "Cloudflare域名管理", Long: "管理Cloudflare上的域名(查询域名列表、查看域名详情等)", } // 域名列表命令 listDomainsCmd := &cobra.Command{ Use: "list", Short: "列出所有域名", Run: func(cmd *cobra.Command, args []string) { log.Info("获取域名列表") client := initCloudflareClient() zones, err := client.ListZones(nil) if err != nil { log.Error("获取域名列表失败: %v", err) os.Exit(1) } if len(zones) == 0 { log.Info("未找到任何域名") return } printDomainsList(zones) }, } // 查看域名详情命令 getDomainCmd := &cobra.Command{ Use: "get [域名]", Short: "获取域名详情", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { domain := args[0] log.Info("获取域名详情: %s", domain) client := initCloudflareClient() zone, err := client.GetZone(domain) if err != nil { log.Error("获取域名详情失败: %v", err) os.Exit(1) } printDomainDetails(zone) }, } // DNS记录管理命令 dnsCmd := &cobra.Command{ Use: "dns", Short: "DNS记录管理", Long: "管理Cloudflare上的DNS记录(查询、创建、更新、删除)", } // 列出DNS记录命令 listDNSCmd := &cobra.Command{ Use: "list [域名]", Short: "列出域名的所有DNS记录", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { domain := args[0] log.Info("获取域名 %s 的DNS记录", domain) client := initCloudflareClient() // 先获取zone ID zone, err := client.GetZone(domain) if err != nil { log.Error("获取域名信息失败: %v", err) os.Exit(1) } records, err := client.ListDNSRecords(zone.ID, nil) if err != nil { log.Error("获取DNS记录失败: %v", err) os.Exit(1) } if len(records) == 0 { log.Info("未找到任何DNS记录") return } printDNSRecordsList(records, domain) }, } // 创建DNS记录命令 createDNSCmd := &cobra.Command{ Use: "create [域名]", Short: "创建DNS记录", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { domain := args[0] log.Info("为域名 %s 创建DNS记录", domain) client := initCloudflareClient() // 先获取zone ID zone, err := client.GetZone(domain) if err != nil { log.Error("获取域名信息失败: %v", err) os.Exit(1) } // 交互式收集DNS记录信息 record, err := collectDNSRecordInfo(domain) if err != nil { log.Error("收集DNS记录信息失败: %v", err) os.Exit(1) } // 确认信息 fmt.Println("\n即将创建以下DNS记录:") fmt.Printf("名称: %s\n", record.Name) fmt.Printf("类型: %s\n", record.Type) fmt.Printf("内容: %s\n", record.Content) fmt.Printf("TTL: %d\n", record.TTL) if record.Priority > 0 { fmt.Printf("优先级: %d\n", record.Priority) } fmt.Printf("代理状态: %v\n", record.Proxied) if !askForConfirmation("确认创建此DNS记录?") { log.Info("已取消创建DNS记录") return } // 创建DNS记录 createdRecord, err := client.CreateDNSRecord(zone.ID, record) if err != nil { log.Error("创建DNS记录失败: %v", err) os.Exit(1) } log.Info("DNS记录创建成功") printDNSRecordDetails(createdRecord, domain) }, } // 更新DNS记录命令 updateDNSCmd := &cobra.Command{ Use: "update [域名]", Short: "更新DNS记录", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { domain := args[0] log.Info("更新域名 %s 的DNS记录", domain) client := initCloudflareClient() // 先获取zone ID zone, err := client.GetZone(domain) if err != nil { log.Error("获取域名信息失败: %v", err) os.Exit(1) } // 获取域名到ID的映射 dnsNameToIDMap := client.GetDNSNameToIDMap(zone.ID) if len(dnsNameToIDMap) == 0 { log.Error("未找到ZoneID %s Zone %s 的任何DNS记录", zone.ID, zone.Name) os.Exit(1) } // recordID 为空,则获取所有DNS记录 recordID := dnsNameToIDMap[domain] if recordID == "" { log.Error("未找到zoneID %s Zone %s 域名 %s 的DNS记录", zone.ID, zone.Name, domain) os.Exit(1) } // 获取当前记录 currentRecord, err := client.GetDNSRecord(zone.ID, recordID) if err != nil { log.Error("获取DNS记录失败: %v", err) os.Exit(1) } // 显示当前记录 fmt.Println("当前DNS记录:") printDNSRecordDetails(currentRecord, domain) // 交互式收集更新后的DNS记录信息 updatedRecord, err := updateDNSRecordInfo(*currentRecord) if err != nil { log.Error("收集DNS记录更新信息失败: %v", err) os.Exit(1) } // 确认信息 fmt.Println("\n即将更新为以下DNS记录:") fmt.Printf("名称: %s\n", updatedRecord.Name) fmt.Printf("类型: %s\n", updatedRecord.Type) fmt.Printf("内容: %s\n", updatedRecord.Content) fmt.Printf("TTL: %d\n", updatedRecord.TTL) if updatedRecord.Priority > 0 { fmt.Printf("优先级: %d\n", updatedRecord.Priority) } fmt.Printf("代理状态: %v\n", updatedRecord.Proxied) if !askForConfirmation("确认更新此DNS记录?") { log.Info("已取消更新DNS记录") return } // 更新DNS记录 updatedRecordResult, err := client.UpdateDNSRecord(zone.ID, recordID, updatedRecord) if err != nil { log.Error("更新DNS记录失败: %v", err) os.Exit(1) } log.Info("DNS记录更新成功") printDNSRecordDetails(updatedRecordResult, domain) }, } // 删除DNS记录命令 deleteDNSCmd := &cobra.Command{ Use: "delete [域名]", Short: "删除DNS记录", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { domain := args[0] log.Info("删除域名 %s 的DNS记录", domain) client := initCloudflareClient() // 先获取zone ID zone, err := client.GetZone(domain) if err != nil { log.Error("获取域名信息失败: %v", err) os.Exit(1) } // 获取域名到ID的映射 dnsNameToIDMap := client.GetDNSNameToIDMap(zone.ID) if len(dnsNameToIDMap) == 0 { log.Error("未找到ZoneID %s Zone %s 的任何DNS记录", zone.ID, zone.Name) os.Exit(1) } // recordID 为空,则获取所有DNS记录 recordID := dnsNameToIDMap[domain] if recordID == "" { log.Error("未找到zoneID %s Zone %s 域名 %s 的DNS记录", zone.ID, zone.Name, domain) os.Exit(1) } // 获取要删除的记录信息 record, err := client.GetDNSRecord(zone.ID, recordID) if err != nil { log.Error("获取DNS记录信息失败: %v", err) os.Exit(1) } // 显示要删除的记录 fmt.Println("即将删除以下DNS记录:") printDNSRecordDetails(record, domain) // 请求确认 if !askForConfirmation("确认删除此DNS记录? 此操作不可恢复!") { log.Info("已取消删除DNS记录") return } // 二次确认 fmt.Println("\n⚠️ 警告: 删除DNS记录可能会影响网站访问、邮件服务等功能") if !askForConfirmation("再次确认删除此DNS记录?") { log.Info("已取消删除DNS记录") return } // 删除DNS记录 success, err := client.DeleteDNSRecord(zone.ID, recordID) if err != nil { log.Error("删除DNS记录失败: %v", err) os.Exit(1) } if success { log.Info("DNS记录删除成功") } else { log.Error("DNS记录删除失败") os.Exit(1) } }, } // 添加域名管理子命令 domainCmd.AddCommand(listDomainsCmd, getDomainCmd) // 添加DNS管理子命令 dnsCmd.AddCommand(listDNSCmd, createDNSCmd, updateDNSCmd, deleteDNSCmd) // 将所有主要命令添加到父命令 cmd.AddCommand(domainCmd, dnsCmd) } // 打印域名列表 func printDomainsList(zones []cloudflare.Zone) { w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "域名\t状态\t名称服务器\t创建时间") fmt.Fprintln(w, "----\t----\t---------\t--------") for _, zone := range zones { nameServers := strings.Join(zone.NameServers, ", ") createdOn := zone.CreatedOn[:10] // 仅显示日期部分 status := zone.Status if zone.Paused { status += " (已暂停)" } fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", zone.Name, status, nameServers, createdOn) } w.Flush() } // 打印域名详情 func printDomainDetails(zone *cloudflare.Zone) { fmt.Printf("================== 域名详情 ==================\n") fmt.Printf("域名: %s\n", zone.Name) fmt.Printf("ID: %s\n", zone.ID) fmt.Printf("状态: %s\n", zone.Status) fmt.Printf("暂停: %v\n", zone.Paused) fmt.Printf("类型: %s\n", zone.Type) fmt.Printf("开发模式: %d\n", zone.DevelopmentMode) fmt.Printf("名称服务器: %s\n", strings.Join(zone.NameServers, ", ")) fmt.Printf("原始名称服务器: %s\n", strings.Join(zone.OriginalNameServers, ", ")) fmt.Printf("修改时间: %s\n", zone.ModifiedOn) fmt.Printf("创建时间: %s\n", zone.CreatedOn) fmt.Printf("激活时间: %s\n", zone.ActivatedOn) if zone.Account.Name != "" { fmt.Printf("账户: %s (%s)\n", zone.Account.Name, zone.Account.ID) } fmt.Printf("==================================================\n") } // 打印DNS记录列表 func printDNSRecordsList(records []cloudflare.DNSRecord, domain string) { w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintf(w, "域名: %s\n\n", domain) fmt.Fprintln(w, "名称\t类型\t内容\tTTL\t代理\t优先级\t备注") fmt.Fprintln(w, "----\t----\t----\t---\t----\t-----\t----") for _, record := range records { ttl := "自动" if record.TTL > 1 { ttl = strconv.Itoa(record.TTL) } priority := "-" if record.Priority > 0 { priority = strconv.Itoa(record.Priority) } proxied := "否" if record.Proxied { proxied = "是" } fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", record.Name, record.Type, record.Content, ttl, proxied, priority, record.Comment) } w.Flush() } // 打印DNS记录详情 func printDNSRecordDetails(record *cloudflare.DNSRecord, domain string) { fmt.Printf("================ DNS记录详情 ================\n") fmt.Printf("域名: %s\n", domain) fmt.Printf("ID: %s\n", record.ID) fmt.Printf("名称: %s\n", record.Name) fmt.Printf("类型: %s\n", record.Type) fmt.Printf("内容: %s\n", record.Content) ttl := "自动" if record.TTL > 1 { ttl = strconv.Itoa(record.TTL) } fmt.Printf("TTL: %s\n", ttl) fmt.Printf("代理: %v\n", record.Proxied) if record.Priority > 0 { fmt.Printf("优先级: %d\n", record.Priority) } if record.Comment != "" { fmt.Printf("备注: %s\n", record.Comment) } if record.CreatedOn != "" { fmt.Printf("创建时间: %s\n", record.CreatedOn) } if record.ModifiedOn != "" { fmt.Printf("修改时间: %s\n", record.ModifiedOn) } fmt.Printf("==============================================\n") } // 交互式收集DNS记录信息 func collectDNSRecordInfo(domain string) (cloudflare.DNSRecord, error) { reader := bufio.NewReader(os.Stdin) record := cloudflare.DNSRecord{ Proxied: false, // 默认不代理 TTL: 1, // 默认自动TTL } // 获取记录名称 fmt.Print("请输入记录名称 (例如: www): ") name, err := reader.ReadString('\n') if err != nil { return record, fmt.Errorf("读取输入失败: %w", err) } name = strings.TrimSpace(name) // 如果用户仅输入@,则使用根域名 if name == "@" { record.Name = domain } else if name != "" { // 否则添加域名后缀 record.Name = name + "." + domain } else { return record, fmt.Errorf("记录名称不能为空") } // 获取记录类型 fmt.Printf("请选择记录类型:\n") fmt.Printf("1) A (IPv4地址)\n") fmt.Printf("2) AAAA (IPv6地址)\n") fmt.Printf("3) CNAME (别名)\n") fmt.Printf("4) TXT (文本记录)\n") fmt.Printf("5) MX (邮件服务器)\n") fmt.Printf("6) SRV (服务记录)\n") fmt.Print("请输入选项 (1-6): ") typeChoice, err := reader.ReadString('\n') if err != nil { return record, fmt.Errorf("读取输入失败: %w", err) } typeChoice = strings.TrimSpace(typeChoice) switch typeChoice { case "1": record.Type = DNSTypeA case "2": record.Type = DNSTypeAAAA case "3": record.Type = DNSTypeCNAME case "4": record.Type = DNSTypeTXT case "5": record.Type = DNSTypeMX case "6": record.Type = DNSTypeSRV default: return record, fmt.Errorf("无效的记录类型选择") } // 获取记录内容 fmt.Printf("请输入记录内容 (%s): ", getContentHint(record.Type)) content, err := reader.ReadString('\n') if err != nil { return record, fmt.Errorf("读取输入失败: %w", err) } content = strings.TrimSpace(content) if content == "" { return record, fmt.Errorf("记录内容不能为空") } record.Content = content // 对于MX记录,获取优先级 if record.Type == DNSTypeMX { fmt.Print("请输入优先级 (1-100,默认10): ") priorityStr, err := reader.ReadString('\n') if err != nil { return record, fmt.Errorf("读取输入失败: %w", err) } priorityStr = strings.TrimSpace(priorityStr) priority := 10 // 默认优先级 if priorityStr != "" { p, err := strconv.Atoi(priorityStr) if err != nil { return record, fmt.Errorf("无效的优先级: %w", err) } if p < 1 || p > 100 { return record, fmt.Errorf("优先级必须在1到100之间") } priority = p } record.Priority = priority } // 获取TTL fmt.Print("请输入TTL (1表示自动,最小值120,建议值1800或3600): ") ttlStr, err := reader.ReadString('\n') if err != nil { return record, fmt.Errorf("读取输入失败: %w", err) } ttlStr = strings.TrimSpace(ttlStr) if ttlStr != "" { ttl, err := strconv.Atoi(ttlStr) if err != nil { return record, fmt.Errorf("无效的TTL: %w", err) } if ttl != 1 && ttl < 120 { return record, fmt.Errorf("TTL不能小于120") } record.TTL = ttl } // 对于A,AAAA或CNAME记录,询问是否代理 if record.Type == DNSTypeA || record.Type == DNSTypeAAAA || record.Type == DNSTypeCNAME { fmt.Print("是否通过Cloudflare代理 (y/n,默认n): ") proxyStr, err := reader.ReadString('\n') if err != nil { return record, fmt.Errorf("读取输入失败: %w", err) } proxyStr = strings.ToLower(strings.TrimSpace(proxyStr)) if proxyStr == "y" || proxyStr == "yes" { record.Proxied = true } } // 获取备注(可选) fmt.Print("请输入备注 (可选): ") comment, err := reader.ReadString('\n') if err != nil { return record, fmt.Errorf("读取输入失败: %w", err) } comment = strings.TrimSpace(comment) if comment != "" { record.Comment = comment } return record, nil } // 更新DNS记录信息 func updateDNSRecordInfo(currentRecord cloudflare.DNSRecord) (cloudflare.DNSRecord, error) { reader := bufio.NewReader(os.Stdin) updatedRecord := currentRecord // 获取记录内容 fmt.Printf("请输入新的记录内容 (%s) [当前值: %s]: ", getContentHint(currentRecord.Type), currentRecord.Content) content, err := reader.ReadString('\n') if err != nil { return updatedRecord, fmt.Errorf("读取输入失败: %w", err) } content = strings.TrimSpace(content) if content != "" { updatedRecord.Content = content } // 对于MX记录,获取优先级 if currentRecord.Type == DNSTypeMX { fmt.Printf("请输入新的优先级 (1-100) [当前值: %d]: ", currentRecord.Priority) priorityStr, err := reader.ReadString('\n') if err != nil { return updatedRecord, fmt.Errorf("读取输入失败: %w", err) } priorityStr = strings.TrimSpace(priorityStr) if priorityStr != "" { p, err := strconv.Atoi(priorityStr) if err != nil { return updatedRecord, fmt.Errorf("无效的优先级: %w", err) } if p < 1 || p > 100 { return updatedRecord, fmt.Errorf("优先级必须在1到100之间") } updatedRecord.Priority = p } } // 获取TTL ttlStr := "自动" if currentRecord.TTL > 1 { ttlStr = strconv.Itoa(currentRecord.TTL) } fmt.Printf("请输入新的TTL (1表示自动,最小值120,建议值1800或3600) [当前值: %s]: ", ttlStr) newTTLStr, err := reader.ReadString('\n') if err != nil { return updatedRecord, fmt.Errorf("读取输入失败: %w", err) } newTTLStr = strings.TrimSpace(newTTLStr) if newTTLStr != "" { ttl, err := strconv.Atoi(newTTLStr) if err != nil { return updatedRecord, fmt.Errorf("无效的TTL: %w", err) } if ttl != 1 && ttl < 120 { return updatedRecord, fmt.Errorf("TTL不能小于120") } updatedRecord.TTL = ttl } // 对于A,AAAA或CNAME记录,询问是否代理 if currentRecord.Type == DNSTypeA || currentRecord.Type == DNSTypeAAAA || currentRecord.Type == DNSTypeCNAME { proxyStr := "n" if currentRecord.Proxied { proxyStr = "y" } fmt.Printf("是否通过Cloudflare代理 (y/n) [当前值: %s]: ", proxyStr) newProxyStr, err := reader.ReadString('\n') if err != nil { return updatedRecord, fmt.Errorf("读取输入失败: %w", err) } newProxyStr = strings.ToLower(strings.TrimSpace(newProxyStr)) if newProxyStr != "" { if newProxyStr == "y" || newProxyStr == "yes" { updatedRecord.Proxied = true } else if newProxyStr == "n" || newProxyStr == "no" { updatedRecord.Proxied = false } } } // 获取备注(可选) fmt.Printf("请输入新的备注 (可选) [当前值: %s]: ", currentRecord.Comment) comment, err := reader.ReadString('\n') if err != nil { return updatedRecord, fmt.Errorf("读取输入失败: %w", err) } comment = strings.TrimSpace(comment) if comment != "" { updatedRecord.Comment = comment } return updatedRecord, nil } // 根据记录类型获取内容提示 func getContentHint(recordType string) string { switch recordType { case DNSTypeA: return "IPv4地址,例如: 192.168.1.1" case DNSTypeAAAA: return "IPv6地址,例如: 2001:db8::1" case DNSTypeCNAME: return "域名,例如: example.com" case DNSTypeTXT: return "文本内容" case DNSTypeMX: return "邮件服务器域名,例如: mail.example.com" case DNSTypeSRV: return "SRV记录,例如: 10 5 5060 sip.example.com" default: return "记录内容" } }