diff --git a/.gitignore b/.gitignore index a339488..4d963fc 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ build .cursor/rules +cmii-uav-watchdog-project-设计结构 + diff --git a/.run/agent-35-70.run.xml b/.run/agent-35-70.run.xml new file mode 100644 index 0000000..5a1d881 --- /dev/null +++ b/.run/agent-35-70.run.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/cmii-uav-watchdog-agent/cmd/env_info.go b/cmii-uav-watchdog-agent/cmd/env_info.go new file mode 100644 index 0000000..d4260bf --- /dev/null +++ b/cmii-uav-watchdog-agent/cmd/env_info.go @@ -0,0 +1,47 @@ +package main + +import ( + "os" + "sync" + + "cmii-uav-watchdog-common/models" +) + +var PodEnv = models.EnvInfo{} + +func init() { + PodEnv = GetEnvInfo() +} + +// GetEnvInfo 获取环境信息 +// 单例模式, 如果已经初始化过, 则直接返回 +func GetEnvInfo() models.EnvInfo { + var once sync.Once + + if PodEnv.K8S_NAMESPACE != "" { + return PodEnv + } + + once.Do(func() { + PodEnv = models.EnvInfo{ + K8S_NAMESPACE: os.Getenv("K8S_NAMESPACE"), + APPLICATION_NAME: os.Getenv("APPLICATION_NAME"), + CUST_JAVA_OPTS: os.Getenv("CUST_JAVA_OPTS"), + BIZ_CONFIG_GROUP: os.Getenv("BIZ_CONFIG_GROUP"), + SYS_CONFIG_GROUP: os.Getenv("SYS_CONFIG_GROUP"), + IMAGE_NAME: os.Getenv("IMAGE_NAME"), + JAVA_VERSION: os.Getenv("JAVA_VERSION"), + GIT_COMMIT: os.Getenv("GIT_COMMIT"), + GIT_BRANCH: os.Getenv("GIT_BRANCH"), + NODE_NAME: os.Getenv("NODE_NAME"), + NODE_IP: os.Getenv("NODE_IP"), + POD_NAME: os.Getenv("POD_NAME"), + LIMIT_CPU: os.Getenv("LIMIT_CPU"), + LIMIT_MEMORY: os.Getenv("LIMIT_MEMORY"), + REQUEST_CPU: os.Getenv("REQUEST_CPU"), + REQUEST_MEMORY: os.Getenv("REQUEST_MEMORY"), + } + }) + + return PodEnv +} diff --git a/cmii-uav-watchdog-agent/cmd/host_info.go b/cmii-uav-watchdog-agent/cmd/host_info.go index 1d619dd..45f52c6 100644 --- a/cmii-uav-watchdog-agent/cmd/host_info.go +++ b/cmii-uav-watchdog-agent/cmd/host_info.go @@ -1 +1,70 @@ -package cmd +package main + +// import ( +// "cmii-uav-watchdog-agent/host_info" +// "cmii-uav-watchdog-common/wdd_log" +// "net/http" +// "os" +// "os/signal" +// "syscall" + +// "github.com/gin-gonic/gin" +// ) + +// func StartHostInfoGin() { +// // 创建一个默认的 Gin 路由 +// var r = gin.Default() // 定义一个 GET 路由 +// r.GET("/ping", func(c *gin.Context) { +// c.JSON(http.StatusOK, gin.H{"message": "pong"}) +// }) + +// // 定义一个 POST 路由 +// r.POST("/echo", func(c *gin.Context) { +// var json map[string]interface{} +// if err := c.ShouldBindJSON(&json); err == nil { +// c.JSON(http.StatusOK, json) +// } else { +// c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) +// } +// }) + +// r.GET("/cpu", func(c *gin.Context) { +// cpuInfo := host_info.GetCPUInfo() // 直接返回 CPU 信息 +// c.JSON(http.StatusOK, cpuInfo) +// }) + +// r.GET("/memory", func(c *gin.Context) { +// memInfo := host_info.GetMemoryInfo() // 直接返回内存信息 +// c.JSON(http.StatusOK, memInfo) +// }) + +// r.GET("/disk", func(c *gin.Context) { +// diskInfo := host_info.GetDiskInfo() // 直接返回磁盘信息 +// c.JSON(http.StatusOK, diskInfo) +// }) + +// r.GET("/motherboard", func(c *gin.Context) { +// mbInfo := host_info.GetMotherboardInfo() // 直接返回主板信息 +// c.JSON(http.StatusOK, mbInfo) +// }) + +// r.GET("/network", func(c *gin.Context) { +// networkInterfaces := host_info.GetNetworkInterfaces() +// c.JSON(http.StatusOK, networkInterfaces) +// }) +// r.GET("/all", func(c *gin.Context) { +// allInfo := host_info.GetAllInfo() +// c.JSON(http.StatusOK, allInfo) +// }) +// //r.GET("/phy", func(c *gin.Context) { +// // allInfo, _ := services.GetPVForLV() +// // c.JSON(http.StatusOK, allInfo) +// //}) +// // 启动服务,监听在 8080 端口 +// r.Run(":8098") +// // 等待终止信号 +// sigs := make(chan os.Signal, 1) +// signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) +// <-sigs +// wdd_log.Info("正在关闭服务...") +// } diff --git a/cmii-uav-watchdog-agent/cmd/start_up.go b/cmii-uav-watchdog-agent/cmd/start_up.go index 88a18ed..ee9ce2d 100644 --- a/cmii-uav-watchdog-agent/cmd/start_up.go +++ b/cmii-uav-watchdog-agent/cmd/start_up.go @@ -1,16 +1,20 @@ -package cmd +package main import ( + "cmii-uav-watchdog-common/wdd_log" "flag" - "log" "os" "os/exec" "os/signal" + "strings" "sync" "syscall" "time" ) +// 是否是Debug模式 +var DebugMode = false + var ( businessProgramType = flag.String("business-program-type", "", "Type of business program (java or python)") businessProgramPath = flag.String("business-program-path", "", "Path to the business program file") @@ -19,45 +23,221 @@ var ( mu sync.Mutex ) +// java 启动参数 +var ( + podName = "unknown" + imageName = "unknown" + imageVersion = "unknown" + workpath = "/cmii" + custJavaOpts = "-Xms200m -Xmx1500m -Djava.awt.headless=true -Dlog4j2.formatMsgNoLookups=true " + envJvmTimezone = "Asia/Shanghai" + k8sNamespace = "default" + applicationName = "app" + nacosRegistry = "helm-nacos:8848" + nacosDiscoveryIp = "127.0.0.1" + nacosDiscoveryPort = "8080" + nacosUsername = "nacos" + nacosPassword = "nacos" + bizConfigGroup = "wdd-biz" + sysConfigGroup = "wdd-sys" +) + +// 初始化 +func init() { + // 获取环境变量 特殊设置才能开启DEBUG + debugMode := os.Getenv("CMII_DEBUG_MODE") + if debugMode == "WDD_DEBUG" { + DebugMode = true + } + wdd_log.Info("DebugMode 是否开启: %v", DebugMode) +} + +// 初始化配置并打印配置信息 +func initConfig() { + // 获取环境变量 + imageName = getEnvOrDefault("IMAGE_NAME", imageName) + if imageName != "unknown" { + parts := strings.Split(imageName, ":") + if len(parts) > 1 { + imageVersion = parts[len(parts)-1] + } + } + workpath = getEnvOrDefault("WORKPATH", workpath) + custJavaOpts = getEnvOrDefault("CUST_JAVA_OPTS", custJavaOpts) + envJvmTimezone = getEnvOrDefault("ENV_JVM_TIMEZONE", envJvmTimezone) + k8sNamespace = getEnvOrDefault("K8S_NAMESPACE", k8sNamespace) + applicationName = getEnvOrDefault("APPLICATION_NAME", applicationName) + nacosRegistry = getEnvOrDefault("NACOS_REGISTRY", nacosRegistry) + nacosDiscoveryIp = getEnvOrDefault("NACOS_DISCOVERY_IP", nacosDiscoveryIp) + nacosDiscoveryPort = getEnvOrDefault("NACOS_DISCOVERY_PORT", nacosDiscoveryPort) + nacosUsername = getEnvOrDefault("NACOS_USERNAME", nacosUsername) + nacosPassword = getEnvOrDefault("NACOS_PASSWORD", nacosPassword) + + // 设置默认配置组 + defaultConfigGroup := "default" + if imageVersion != "unknown" { + parts := strings.Split(imageVersion, "-") + if len(parts) > 0 { + defaultConfigGroup = parts[0] + } + } + + bizConfigGroup = getEnvOrDefault("BIZ_CONFIG_GROUP", bizConfigGroup) + if bizConfigGroup == "" { + wdd_log.Info("[CONTAINER] As BIZ_CONFIG_GROUP is null, it set default value [%s]", defaultConfigGroup) + bizConfigGroup = defaultConfigGroup + os.Setenv("BIZ_CONFIG_GROUP", bizConfigGroup) + } + + sysConfigGroup = getEnvOrDefault("SYS_CONFIG_GROUP", sysConfigGroup) + if sysConfigGroup == "" { + wdd_log.Info("[CONTAINER] As SYS_CONFIG_GROUP is null, it set default value [%s]", defaultConfigGroup) + sysConfigGroup = defaultConfigGroup + os.Setenv("SYS_CONFIG_GROUP", sysConfigGroup) + } + + // 打印配置信息 + wdd_log.Info("[CONTAINER] %s image is running ...", imageName) + wdd_log.Info("[CONTAINER] IMAGE_VERSION is %s", imageVersion) + wdd_log.Info("[CONTAINER] WORKPATH is %s", workpath) + wdd_log.Info("[CONTAINER] CUST_JAVA_OPTS is %s", custJavaOpts) + wdd_log.Info("[CONTAINER] JVM_TIMEZONE is %s", envJvmTimezone) + wdd_log.Info("[CONTAINER] K8S_NAMESPACE is %s", k8sNamespace) + wdd_log.Info("[CONTAINER] APPLICATION_NAME is %s", applicationName) + wdd_log.Info("[CONTAINER] NACOS_REGISTRY is %s", nacosRegistry) + wdd_log.Info("[CONTAINER] NACOS_DISCOVERY_IP is %s", nacosDiscoveryIp) + wdd_log.Info("[CONTAINER] NACOS_DISCOVERY_PORT is %s", nacosDiscoveryPort) + wdd_log.Info("[CONTAINER] NACOS_USERNAME is %s", nacosUsername) + wdd_log.Info("[CONTAINER] NACOS_PASSWORD is %s", nacosPassword) + wdd_log.Info("[CONTAINER] BIZ_CONFIG_GROUP is %s", bizConfigGroup) + wdd_log.Info("[CONTAINER] SYS_CONFIG_GROUP is %s", sysConfigGroup) + wdd_log.Info("[CONTAINER] starting...") +} + +// 获取环境变量,如果为空则返回默认值 +func getEnvOrDefault(key, defaultValue string) string { + value := os.Getenv(key) + if value == "" { + return defaultValue + } + return value +} + func startBusinessProcess(programType, programPath string) *exec.Cmd { var cmd *exec.Cmd switch programType { case "java": - cmd = exec.Command("java", "-jar", programPath) + // 初始化配置 + initConfig() + + // 构建命令参数列表 + args := []string{} + + // 添加Java选项 + if custJavaOpts != "" { + // 分割CUST_JAVA_OPTS中的多个参数 + for _, opt := range splitArgs(custJavaOpts) { + if opt != "" { + args = append(args, opt) + } + } + } + + // 添加主JAR文件 + args = append(args, "-jar", programPath) + + // 添加其他参数 + args = append(args, []string{ + "--user.timezone=" + envJvmTimezone, + "-Dfile.encoding=UTF-8", + "--spring.main.allow-bean-definition-overriding=true", + "--spring.application.name=" + applicationName, + "--spring.cloud.nacos.username=" + nacosUsername, + "--spring.cloud.nacos.password=" + nacosPassword, + "--spring.cloud.nacos.config.server-addr=" + nacosRegistry, + "--spring.cloud.nacos.config.extension-configs[0].data-id=" + applicationName + ".yml", + "--spring.cloud.nacos.config.extension-configs[0].group=" + bizConfigGroup, + "--spring.cloud.nacos.config.extension-configs[0].refresh=true", + "--spring.cloud.nacos.config.shared-configs[0].data-id=cmii-backend-system.yml", + "--spring.cloud.nacos.config.shared-configs[0].group=" + sysConfigGroup, + "--spring.cloud.nacos.config.shared-configs[0].refresh=true", + "--spring.cloud.nacos.discovery.server-addr=" + nacosRegistry, + "--spring.cloud.nacos.discovery.ip=" + nacosDiscoveryIp, + "--spring.cloud.nacos.discovery.port=" + nacosDiscoveryPort, + }...) + + wdd_log.Info("[CONTAINER] java args: %v", args) + + cmd = exec.Command("java", args...) case "python": cmd = exec.Command("python", programPath) default: - log.Fatalf("Unsupported business program type: %s", programType) + wdd_log.Error("不支持的业务程序类型: %s", programType) } return cmd } +// 分割命令行参数 +func splitArgs(s string) []string { + var args []string + var inQuote bool + var current string + var quoteChar rune + + for _, r := range s { + switch { + case (r == '"' || r == '\'') && !inQuote: + inQuote = true + quoteChar = r + case r == quoteChar && inQuote: + inQuote = false + quoteChar = 0 + case r == ' ' && !inQuote: + if current != "" { + args = append(args, current) + current = "" + } + default: + current += string(r) + } + } + + if current != "" { + args = append(args, current) + } + + return args +} + func main() { // 解析命令行参数 flag.Parse() if *businessProgramType == "" || *businessProgramPath == "" { - log.Fatal("Missing required flags: -business-program-type and -business-program-path must be specified") + wdd_log.Error("缺少必要的参数: -business-program-type 和 -business-program-path 必须指定") } // 信号处理 signalChan := make(chan os.Signal, 1) signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM) + + // StartHeartbeatDetection(signalChan) + go func() { for sig := range signalChan { - log.Printf("Received signal: %v", sig) + wdd_log.Info("接收到信号: %v", sig) mu.Lock() stopRequested = true if currentCmd != nil && currentCmd.Process != nil { // 发送 SIGTERM 给业务进程 if err := currentCmd.Process.Signal(syscall.SIGTERM); err != nil { - log.Printf("Failed to send SIGTERM to process: %v", err) + wdd_log.Error("向进程发送SIGTERM信号失败: %v", err) } // 等待 10 秒后强制杀死进程 time.AfterFunc(10*time.Second, func() { mu.Lock() defer mu.Unlock() if currentCmd != nil && currentCmd.Process != nil { - log.Println("Graceful shutdown timeout, sending SIGKILL") + wdd_log.Warn("优雅关闭超时,发送SIGKILL信号") currentCmd.Process.Kill() } }) @@ -66,12 +246,19 @@ func main() { } }() + // 授权检测 + go func() { + for { + StartHeartbeatDetection(signalChan) + } + }() + // 主循环 for { mu.Lock() if stopRequested { mu.Unlock() - log.Println("Shutting down due to stop request") + wdd_log.Info("收到停止请求,正在关闭") os.Exit(0) } mu.Unlock() @@ -82,11 +269,16 @@ func main() { // 启动业务进程 if err := cmd.Start(); err != nil { - log.Printf("Failed to start business process: %v", err) + wdd_log.Error("启动业务进程失败: %v", err) time.Sleep(5 * time.Second) continue } + // 业务进程启动成功 + if *businessProgramType == "java" { + wdd_log.Info("[CONTAINER] 程序启动成功!") + } + mu.Lock() currentCmd = cmd mu.Unlock() @@ -98,21 +290,21 @@ func main() { mu.Unlock() if err != nil { - log.Printf("Business process exited with error: %v", err) + wdd_log.Error("业务进程异常退出: %v", err) } else { - log.Println("Business process exited normally") + wdd_log.Info("业务进程正常退出") } mu.Lock() if stopRequested { mu.Unlock() - log.Println("Shutting down due to stop request") + wdd_log.Info("收到停止请求,正在关闭") os.Exit(0) } mu.Unlock() // 等待 5 秒后重启 - log.Println("Restarting business process in 5 seconds...") + wdd_log.Info("5秒后重启业务进程...") time.Sleep(5 * time.Second) } } diff --git a/cmii-uav-watchdog-agent/cmd/watchdog-agent.go b/cmii-uav-watchdog-agent/cmd/watchdog-agent.go index 6288f86..cdf31bf 100644 --- a/cmii-uav-watchdog-agent/cmd/watchdog-agent.go +++ b/cmii-uav-watchdog-agent/cmd/watchdog-agent.go @@ -1,76 +1,95 @@ -package cmd +package main import ( "cmii-uav-watchdog-agent/host_info" "cmii-uav-watchdog-agent/rpc" - "cmii-uav-watchdog-agent/totp" "cmii-uav-watchdog-common/models" - "fmt" - "log" + "cmii-uav-watchdog-common/totp_tier_two" + "cmii-uav-watchdog-common/wdd_log" "os" - "os/signal" "syscall" "time" ) -const ( +var ( // 最大重试次数 - maxRetryCount = 5 + maxRetryCount = 12 // 默认心跳检测间隔 - defaultHeartbeatInterval = 30 * time.Second + defaultHeartbeatInterval = 2 * time.Hour // 检测失败后的等待间隔 - failWaitInterval = 5 * time.Second - - // 环境变量名称 - appNameEnv = "APP_NAME" + failWaitInterval = 1 * time.Hour ) -// 启动心跳检测 -func StartHeartbeatDetection() { - log.Println("启动心跳检测任务...") +var tierTwoTotpSecret = "" + +// StartHeartbeatDetection 启动心跳检测 +func StartHeartbeatDetection(signalChan chan os.Signal) { + wdd_log.Info("启动心跳检测任务...") + + // variable + var err error + + // 如果Debug模式,那么使用环境变量赋值 + if DebugMode { + // 如果从在环境变量 那么使用环境变量赋值 + heartbeatInterval := os.Getenv("WATCHDOG_AGENT_HEARTBEAT_INTERVAL") + if heartbeatInterval != "" { + defaultHeartbeatInterval, err = time.ParseDuration(heartbeatInterval) + if err != nil { + wdd_log.Error("无法解析环境变量: %v", err) + } + wdd_log.Info("已更新心跳检测间隔 => %s", defaultHeartbeatInterval) + } + + failWaitIntervalEnv := os.Getenv("WATCHDOG_AGENT_FAIL_WAIT_INTERVAL") + if failWaitIntervalEnv != "" { + failWaitInterval, err = time.ParseDuration(failWaitIntervalEnv) + if err != nil { + wdd_log.Error("无法解析环境变量: %v", err) + } + wdd_log.Info("已更新心跳检测失败等待间隔 => %s", failWaitInterval) + } + + // + } // 创建RPC客户端 - client := rpc.NewClient(nil) + heartbeatURL := os.Getenv("WATCHDOG_HEARTBEAT_URL") + client := rpc.NewClient(nil, heartbeatURL) - // 监听终止信号 - signalChan := make(chan os.Signal, 1) - signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM) + wdd_log.Info("心跳检测URL: %s", client.GetHeartbeatURL()) // 失败计数器 - failCount := 0 + failCount := 1 // 心跳检测循环 for { select { case <-signalChan: - log.Println("收到终止信号,停止心跳检测") + wdd_log.Info("收到终止信号,停止心跳检测") return default: // 尝试发送心跳请求 authorized, err := sendHeartbeat(client) if err != nil { - log.Printf("心跳检测失败: %v", err) + wdd_log.Error("第 %d 次心跳检测失败: %v", failCount, err) failCount++ } else if !authorized { - log.Println("未获得授权") + wdd_log.Warn("第 %d 次心跳检测未获得授权", failCount) failCount++ } else { // 检测成功,重置失败计数 - failCount = 0 - log.Println("心跳检测成功,已获得授权") + failCount = 1 + wdd_log.Info("第 %d 次心跳检测成功,已获得授权", failCount) } // 检查是否达到最大失败次数 if failCount >= maxRetryCount { - log.Printf("心跳检测连续失败 %d 次,发送终止信号", failCount) - // 发送终止信号给start_up.go - process, err := os.FindProcess(os.Getpid()) - if err == nil { - process.Signal(syscall.SIGTERM) - } + wdd_log.Fatal("心跳检测连续失败 %d 次,发送终止信号!!", failCount) + signalChan <- syscall.SIGTERM return } @@ -88,8 +107,9 @@ func StartHeartbeatDetection() { // 发送心跳请求 func sendHeartbeat(client *rpc.Client) (bool, error) { + // 1. 获取主机信息 - hostInfoData := services.GetAllInfo() + hostInfoData := host_info.GetAllInfo() hostInfo := models.HostInfo{ SystemInfo: hostInfoData.SystemInfo, CPUInfo: hostInfoData.CPUInfo, @@ -98,26 +118,21 @@ func sendHeartbeat(client *rpc.Client) (bool, error) { NetInfo: hostInfoData.NetInfo, } - // 2. 获取应用名称 - appName := os.Getenv(appNameEnv) - if appName == "" { - appName = "unknown-app" - log.Printf("警告: 环境变量 %s 未设置,使用默认值: %s", appNameEnv, appName) - } + // 获取环境信息 + envInfo := GetEnvInfo() // 构建心跳请求 request := &models.HeartbeatRequest{ HostInfo: hostInfo, Timestamp: time.Now().Unix(), - AppName: appName, + EnvInfo: envInfo, } // 3. 如果已有TOTP密钥,则生成TOTP验证码 - totpSecret := totp.GetTOTPSecret() - if totpSecret != "" { - totpCode, err := totp.GenerateTOTPCode() + if tierTwoTotpSecret != "" { + totpCode, err := totp_tier_two.GenerateTierTwoTOTPCode(tierTwoTotpSecret) if err != nil { - log.Printf("生成TOTP验证码失败: %v", err) + wdd_log.Error("生成TOTP验证码失败: %v", err) } else { request.TOTPCode = totpCode } @@ -126,20 +141,24 @@ func sendHeartbeat(client *rpc.Client) (bool, error) { // 4. 发送心跳请求 response, err := client.SendHeartbeatWithRetry(request, 10*time.Second) if err != nil { - return false, fmt.Errorf("发送心跳请求失败: %w", err) + return false, err + } + + if response == nil { + return false, err } // 5. 处理响应 if response.SecondTOTPSecret != "" { // 存储TOTP密钥 - totp.SetTOTPSecret(response.SecondTOTPSecret) - log.Println("已更新TOTP密钥") + tierTwoTotpSecret = response.SecondTOTPSecret + wdd_log.Info("已更新TOTP密钥 => %s", tierTwoTotpSecret) } // 6. 如果有TOTP验证码,进行验证 - if response.TOTPCode != "" && totpSecret != "" { - if !totp.ValidateTOTPCode(response.TOTPCode) { - log.Println("TOTP验证码验证失败") + if response.TOTPCode != "" && tierTwoTotpSecret != "" { + if !totp_tier_two.VerifyTierTwoTOTPCode(response.TOTPCode, tierTwoTotpSecret) { + wdd_log.Warn("TOTP验证码验证失败") return false, nil } } diff --git a/cmii-uav-watchdog-agent/go.mod b/cmii-uav-watchdog-agent/go.mod index 726a469..52e4921 100644 --- a/cmii-uav-watchdog-agent/go.mod +++ b/cmii-uav-watchdog-agent/go.mod @@ -2,38 +2,10 @@ module cmii-uav-watchdog-agent go 1.24 -require ( - cmii-uav-watchdog-common v0.0.0 - github.com/gin-gonic/gin v1.10.0 -) +require cmii-uav-watchdog-common v0.0.0 -require ( - github.com/bytedance/sonic v1.11.6 // indirect - github.com/bytedance/sonic/loader v0.1.1 // indirect - github.com/cloudwego/base64x v0.1.4 // indirect - github.com/cloudwego/iasm v0.2.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.3 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.20.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect - github.com/leodido/go-urn v1.4.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.12 // indirect - golang.org/x/arch v0.8.0 // indirect - golang.org/x/crypto v0.23.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - google.golang.org/protobuf v1.34.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) +require cmii-uav-watchdog-otp v0.0.0 // indirect replace cmii-uav-watchdog-common => ../cmii-uav-watchdog-common + +replace cmii-uav-watchdog-otp => ../cmii-uav-watchdog-otp diff --git a/cmii-uav-watchdog-agent/go.sum b/cmii-uav-watchdog-agent/go.sum index 7f08abb..e69de29 100644 --- a/cmii-uav-watchdog-agent/go.sum +++ b/cmii-uav-watchdog-agent/go.sum @@ -1,89 +0,0 @@ -github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= -github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= -github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= -github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= -github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= -github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= -github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= -github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= -github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= -github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= -golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/cmii-uav-watchdog-agent/host_info/cpu_service.go b/cmii-uav-watchdog-agent/host_info/cpu_service.go index 2ae2348..10bbf60 100644 --- a/cmii-uav-watchdog-agent/host_info/cpu_service.go +++ b/cmii-uav-watchdog-agent/host_info/cpu_service.go @@ -1,163 +1,205 @@ -package services +package host_info import ( "bufio" "cmii-uav-watchdog-common/models" - "fmt" + "cmii-uav-watchdog-common/wdd_log" "os" + "os/exec" + "runtime" + "strconv" "strings" ) // NewCPUInfo 创建一个默认的 CPUInfo func NewCPUInfo() models.CPUInfo { return models.CPUInfo{ - ModelName: "unknown", - Cores: 0, - Architecture: "unknown", - UUID: "unknown", + ModelName: "unknown", + Cores: 0, + Architecture: "amd64", + Flags: []string{}, + Hypervisor: "unknown", + Virtualization: "unknown", } } -const ( - exePath = "/proc/self/exe" - elfMagic = 0x7f - elfMagicString = "ELF" - eMachineOffset = 18 -) - -// parseLine 解析单行 CPU 信息 -func parseLine(line string, cpuInfo *models.CPUInfo) { - if strings.HasPrefix(line, "model name") { - cpuInfo.ModelName = strings.TrimSpace(strings.Split(line, ":")[1]) - } -} - -// getCPUCores 通过 processor 行获取 CPU 核心数 -func getCPUCores() (int, error) { - cores := 0 - file, err := os.Open("/proc/cpuinfo") - if err != nil { - return cores, err - } - defer file.Close() - - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := scanner.Text() - if strings.HasPrefix(line, "processor") { - cores++ - } - } - - if err := scanner.Err(); err != nil { - return cores, err - } - - return cores, nil -} - -// getCPUUUID 获取 CPU UUID -func getCPUUUID() (string, error) { - data, err := os.ReadFile("/sys/class/dmi/id/product_uuid") - if err != nil { - return "unknown", err - } - return strings.TrimSpace(string(data)), nil -} - -func getCPUArchitecture() string { - cpuarch := "unknown" - - // 打开当前进程的执行文件 - file, err := os.Open(exePath) - if err != nil { - _, _ = fmt.Fprintf(os.Stderr, "Error opening %s: %v\n", exePath, err) - return cpuarch - } - defer file.Close() - - // 读取前64个字节以获取 ELF 头部信息 - header := make([]byte, 64) - if _, err := file.Read(header); err != nil { - _, _ = fmt.Fprintf(os.Stderr, "Error reading header: %v\n", err) - return cpuarch - } - - // 检查 ELF 文件标识 - if header[0] != elfMagic || string(header[1:4]) != elfMagicString { - _, _ = fmt.Fprintln(os.Stderr, "File is not an ELF file") - return cpuarch - } - - // 获取架构信息 - arch := header[eMachineOffset] // e_machine 字段的偏移量 - switch arch { - case 0x02: // EM_386 - cpuarch = "x86(32-bit)" - case 0x03: // EM_X86_64 - cpuarch = "x86_64(64-bit)" - case 0x28: // EM_ARM - cpuarch = "ARM" - case 0x2A: // EM_ARM64 - cpuarch = "ARM64" - case 0x08: // EM_MIPS - cpuarch = "MIPS" - default: - cpuarch = "unknown architecture" - } - - return cpuarch -} - -// GetCPUInfo 获取 CPU 信息 +// GetCPUInfo 获取CPU信息 +// 返回CPU信息结构体 func GetCPUInfo() models.CPUInfo { + wdd_log.Debug("开始获取CPU信息") + + // 首先尝试从/proc/cpuinfo文件中读取信息 + cpuInfo, err := getCPUInfoFromProc() + if err == nil { + wdd_log.Debug("成功从/proc/cpuinfo获取CPU信息") + return cpuInfo + } + + // 如果从文件获取失败,尝试使用lscpu命令 + wdd_log.Warn("从/proc/cpuinfo获取CPU信息失败: %s,尝试使用lscpu命令", err.Error()) + cpuInfo, err = getCPUInfoFromLscpu() + if err == nil { + wdd_log.Debug("成功从lscpu命令获取CPU信息") + return cpuInfo + } + + // 如果都失败,使用runtime包获取基本信息 + wdd_log.Warn("从lscpu获取CPU信息失败: %s,使用runtime包获取基本信息", err.Error()) + return getFallbackCPUInfo() +} + +// getCPUInfoFromProc 从/proc/cpuinfo文件中读取CPU信息 +// 返回CPU信息结构体和可能的错误 +func getCPUInfoFromProc() (models.CPUInfo, error) { + wdd_log.Debug("尝试从/proc/cpuinfo读取CPU信息") + + // 创建默认的CPU信息结构体 cpuInfo := NewCPUInfo() - // 读取 /proc/cpuinfo + // 打开/proc/cpuinfo文件 file, err := os.Open("/proc/cpuinfo") if err != nil { - fmt.Println("Error opening /proc/cpuinfo:", err) - return cpuInfo // 返回默认值 + return cpuInfo, err } - defer func() { - if err := file.Close(); err != nil { - fmt.Println("Error closing /proc/cpuinfo:", err) - } - }() + defer file.Close() + // 使用scanner读取文件 scanner := bufio.NewScanner(file) + + // 处理的CPU核心数量 + coreCount := 1 + var flags []string + + // 逐行读取并解析 for scanner.Scan() { - parseLine(scanner.Text(), &cpuInfo) - } - if err := scanner.Err(); err != nil { - fmt.Println("Error reading /proc/cpuinfo:", err) - return cpuInfo // 返回默认值 + line := scanner.Text() + parts := strings.Split(line, ":") + + if len(parts) < 2 { + continue + } + + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + + switch key { + case "processor": + // 计数处理器核心数量 + coreCount++ + case "model name": + // 获取CPU型号名称 + cpuInfo.ModelName = value + // 提取频率信息(如果包含) + if strings.Contains(value, "@") { + freqParts := strings.Split(value, "@") + if len(freqParts) > 1 { + cpuInfo.Frequency = strings.TrimSpace(freqParts[1]) + } + } + case "flags": + // 获取CPU标志 + flags = strings.Fields(value) + case "cpu cores": + // 获取每个物理CPU的核心数 + cores, err := strconv.Atoi(value) + if err == nil && cores > 0 { + cpuInfo.Cores = cores + } + } } - // 获取 CPU 核心数 - cores, err := getCPUCores() + // 如果没有从cpu cores字段获取到核心数,使用处理器计数 + if cpuInfo.Cores == 0 && coreCount > 0 { + cpuInfo.Cores = coreCount + } + + // 设置CPU标志 + cpuInfo.Flags = flags + + // 检测虚拟化相关信息 + for _, flag := range flags { + if flag == "vmx" { + cpuInfo.Virtualization = "vmx" + } else if flag == "svm" { + cpuInfo.Virtualization = "svm" + } else if flag == "hypervisor" { + cpuInfo.Hypervisor = "present" + } + } + + // 设置架构 + cpuInfo.Architecture = runtime.GOARCH + + return cpuInfo, nil +} + +// getCPUInfoFromLscpu 使用lscpu命令获取CPU信息 +// 返回CPU信息结构体和可能的错误 +func getCPUInfoFromLscpu() (models.CPUInfo, error) { + wdd_log.Debug("尝试使用lscpu命令获取CPU信息") + + // 创建默认的CPU信息结构体 + cpuInfo := NewCPUInfo() + + // 执行lscpu命令 + cmd := exec.Command("lscpu") + output, err := cmd.Output() if err != nil { - fmt.Println("Error getting CPU cores:", err) - } else { - cpuInfo.Cores = cores + return cpuInfo, err } - // 获取 CPU UUID - uuid, err := getCPUUUID() - if err != nil { - fmt.Println("Error getting CPU UUID:", err) - } else { - cpuInfo.UUID = uuid + // 解析输出 + lines := strings.Split(string(output), "\n") + for _, line := range lines { + parts := strings.Split(line, ":") + if len(parts) < 2 { + continue + } + + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + + switch key { + case "Model name": + cpuInfo.ModelName = value + // 提取频率信息(如果包含) + if strings.Contains(value, "@") { + freqParts := strings.Split(value, "@") + if len(freqParts) > 1 { + cpuInfo.Frequency = strings.TrimSpace(freqParts[1]) + } + } + case "Architecture": + cpuInfo.Architecture = value + case "CPU(s)": + cores, err := strconv.Atoi(value) + if err == nil { + cpuInfo.Cores = cores + } + case "Flags": + cpuInfo.Flags = strings.Fields(value) + case "Hypervisor vendor": + cpuInfo.Hypervisor = value + case "Virtualization": + cpuInfo.Virtualization = value + case "CPU MHz": + if cpuInfo.Frequency == "" { + cpuInfo.Frequency = value + " MHz" + } + } } - cpuInfo.Architecture = getCPUArchitecture() + return cpuInfo, nil +} + +// getFallbackCPUInfo 使用runtime包获取基本CPU信息 +// 返回CPU信息结构体 +func getFallbackCPUInfo() models.CPUInfo { + wdd_log.Debug("使用runtime包获取基本CPU信息") + + cpuInfo := NewCPUInfo() + cpuInfo.Cores = runtime.NumCPU() + cpuInfo.Architecture = runtime.GOARCH return cpuInfo } - -/* -CPU模型名称: Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz -核心数量: 4 -架构信息: x86_64 - -*/ diff --git a/cmii-uav-watchdog-agent/host_info/disk_service.go b/cmii-uav-watchdog-agent/host_info/disk_service.go index a25cd87..f875d62 100644 --- a/cmii-uav-watchdog-agent/host_info/disk_service.go +++ b/cmii-uav-watchdog-agent/host_info/disk_service.go @@ -1,8 +1,8 @@ -package services +package host_info import ( "cmii-uav-watchdog-common/models" - "fmt" + "cmii-uav-watchdog-common/wdd_log" "strconv" "strings" "syscall" @@ -68,7 +68,7 @@ func GetDiskInfo() []models.DiskInfo { // Get disk usage information using stat var stat syscall.Statfs_t if err := syscall.Statfs(mountPoint, &stat); err != nil { - fmt.Printf("Warning: error getting statfs for %s: %v\n", mountPoint, err) + wdd_log.Warn("获取 %s 的statfs信息失败: %v", mountPoint, err) continue } @@ -85,7 +85,8 @@ func GetDiskInfo() []models.DiskInfo { // Calculate percentage used usePercent := "0%" if total > 0 { - usePercent = fmt.Sprintf("%.1f%%", float64(used)/float64(total)*100) + percentage := float64(used) / float64(total) * 100 + usePercent = strconv.FormatFloat(percentage, 'f', 1, 64) + "%" } // Determine the physical device and its size diff --git a/cmii-uav-watchdog-agent/host_info/host_info.go b/cmii-uav-watchdog-agent/host_info/host_info.go index b758318..8b39ff9 100644 --- a/cmii-uav-watchdog-agent/host_info/host_info.go +++ b/cmii-uav-watchdog-agent/host_info/host_info.go @@ -1,4 +1,4 @@ -package services +package host_info import "cmii-uav-watchdog-common/models" diff --git a/cmii-uav-watchdog-agent/host_info/mboard_service.go b/cmii-uav-watchdog-agent/host_info/mboard_service.go index 5e15f3e..d9f6081 100644 --- a/cmii-uav-watchdog-agent/host_info/mboard_service.go +++ b/cmii-uav-watchdog-agent/host_info/mboard_service.go @@ -1,8 +1,8 @@ -package services +package host_info import ( "cmii-uav-watchdog-common/models" - "fmt" + "cmii-uav-watchdog-common/wdd_log" "os" "strings" ) @@ -19,7 +19,7 @@ var DefaultMotherboardInfo = models.MotherboardInfo{ func readFileWithWarning(path string) ([]byte, error) { value, err := os.ReadFile(path) if err != nil { - fmt.Printf("Warning: unable to read file %s: %v\n", path, err) + wdd_log.Warn("无法读取文件 %s: %v", path, err) return nil, err } return value, nil diff --git a/cmii-uav-watchdog-agent/host_info/mem_service.go b/cmii-uav-watchdog-agent/host_info/mem_service.go index 73bf0f9..89d8ca5 100644 --- a/cmii-uav-watchdog-agent/host_info/mem_service.go +++ b/cmii-uav-watchdog-agent/host_info/mem_service.go @@ -1,8 +1,8 @@ -package services +package host_info import ( "cmii-uav-watchdog-common/models" - "fmt" + "cmii-uav-watchdog-common/wdd_log" "os" "strconv" "strings" @@ -26,7 +26,7 @@ func GetMemoryInfo() models.MemoryInfo { memInfo := NewMemoryInfo() data, err := os.ReadFile("/proc/meminfo") if err != nil { - fmt.Println("Error reading /proc/meminfo:", err) + wdd_log.Error("读取 /proc/meminfo 失败: %v", err) return memInfo } @@ -39,7 +39,7 @@ func GetMemoryInfo() models.MemoryInfo { key := fields[0] value, err := strconv.ParseUint(fields[1], 10, 64) if err != nil { - fmt.Printf("Error parsing value for %s: %v\n", key, err) + wdd_log.Error("解析 %s 的值失败: %v", key, err) continue } diff --git a/cmii-uav-watchdog-agent/host_info/net_service.go b/cmii-uav-watchdog-agent/host_info/net_service.go index 393c865..220f7d9 100644 --- a/cmii-uav-watchdog-agent/host_info/net_service.go +++ b/cmii-uav-watchdog-agent/host_info/net_service.go @@ -1,8 +1,8 @@ -package services +package host_info import ( "cmii-uav-watchdog-common/models" - "fmt" + "cmii-uav-watchdog-common/wdd_log" "net" "strings" ) @@ -25,7 +25,7 @@ func GetNetworkInterfaces() []models.NetworkInterfaceInfo { // 获取所有网络接口 ifaces, err := net.Interfaces() if err != nil { - fmt.Println("Error getting network interfaces:", err) + wdd_log.Error("获取网络接口失败: %v", err) return []models.NetworkInterfaceInfo{{Name: "unknown", MACAddress: "00:00:00:00:00:00", IPAddresses: []string{}}} } @@ -40,7 +40,7 @@ func GetNetworkInterfaces() []models.NetworkInterfaceInfo { // 获取 MAC 地址 macAddress, err := getMACAddress(iface) if err != nil { - fmt.Println("Error getting MAC address for", iface.Name, ":", err) + wdd_log.Error("获取网卡 %s 的MAC地址失败: %v", iface.Name, err) continue } @@ -48,7 +48,7 @@ func GetNetworkInterfaces() []models.NetworkInterfaceInfo { var ipAddresses []string addrs, err := iface.Addrs() if err != nil { - fmt.Println("Error getting addresses for", iface.Name, ":", err) + wdd_log.Error("获取网卡 %s 的IP地址失败: %v", iface.Name, err) continue } diff --git a/cmii-uav-watchdog-agent/host_info/system_service.go b/cmii-uav-watchdog-agent/host_info/system_service.go index 2ac613a..7a87531 100644 --- a/cmii-uav-watchdog-agent/host_info/system_service.go +++ b/cmii-uav-watchdog-agent/host_info/system_service.go @@ -1,8 +1,8 @@ -package services +package host_info import ( "cmii-uav-watchdog-common/models" - "log" + "cmii-uav-watchdog-common/wdd_log" "os" "strings" ) @@ -10,16 +10,12 @@ import ( // NewOSInfo 创建并返回一个 OSInfo 实例,设置默认值 func NewOSInfo() models.OSInfo { return models.OSInfo{ - Name: "unknown", - Version: "unknown", - ID: "unknown", - IDLike: "unknown", - VersionID: "unknown", - PrettyName: "unknown", - HomeURL: "unknown", - SupportURL: "unknown", - BugReportURL: "unknown", - PrivacyURL: "unknown", + Name: "unknown", + Version: "unknown", + ID: "unknown", + IDLike: "unknown", + VersionID: "unknown", + PrettyName: "unknown", } } @@ -41,6 +37,14 @@ func GetSystemInfo() models.SystemInfo { if machineID != "" { sysInfo.MachineID = machineID } + + // 获取 machine serial + + serialID := getMachineSerial() + if serialID != "" { + sysInfo.MachineSerial = serialID + } + // 获取操作系统版本 sysInfo.OS = getOSVersion() @@ -50,12 +54,26 @@ func GetSystemInfo() models.SystemInfo { sysInfo.KernelVersion = kernelVersion } + // 获取节点名称 + sysInfo.NodeName = "unknown" + nodeName := os.Getenv("NODE_NAME") + if nodeName != "" { + sysInfo.NodeName = nodeName + } + + // 获取节点IP + sysInfo.NodeIP = "unknown" + ip := os.Getenv("NODE_IP") + if ip != "" { + sysInfo.NodeIP = ip + } + return sysInfo } func getOSVersion() models.OSInfo { data, err := os.ReadFile("/etc/os-release") if err != nil { - log.Printf("Error reading /etc/os-release: %v", err) + wdd_log.Error("读取 /etc/os-release 失败: %v", err) return NewOSInfo() // 返回默认值 } @@ -88,25 +106,27 @@ func getOSVersion() models.OSInfo { osInfo.VersionID = value case "PRETTY_NAME": osInfo.PrettyName = value - case "HOME_URL": - osInfo.HomeURL = value - case "SUPPORT_URL": - osInfo.SupportURL = value - case "BUG_REPORT_URL": - osInfo.BugReportURL = value - case "PRIVACY_URL": - osInfo.PrivacyURL = value } } return osInfo } -// getMachineID 从 /etc/machine-id 文件中获取机器 ID +// getMachineID 从 /sys/devices/virtual/dmi/id/product_uuid 文件中获取机器 ID func getMachineID() string { - data, err := os.ReadFile("/etc/machine-id") + data, err := os.ReadFile("/sys/devices/virtual/dmi/id/product_uuid") if err != nil { - log.Printf("Error reading /etc/machine-id: %v", err) + wdd_log.Error("读取 /sys/devices/virtual/dmi/id/product_uuid 失败: %v", err) + return "" + } + return strings.TrimSpace(string(data)) // 去除多余空格 +} + +// getMachineID 从 /sys/devices/virtual/dmi/id/product_uuid 文件中获取机器 ID +func getMachineSerial() string { + data, err := os.ReadFile("/sys/devices/virtual/dmi/id/product_serial") + if err != nil { + wdd_log.Error("读取 /sys/devices/virtual/dmi/id/product_serial 失败: %v", err) return "" } return strings.TrimSpace(string(data)) // 去除多余空格 @@ -116,8 +136,8 @@ func getMachineID() string { func getKernelVersion() string { data, err := os.ReadFile("/proc/version") if err != nil { - log.Printf("Error reading /proc/version: %v", err) + wdd_log.Error("读取 /proc/version 失败: %v", err) return "" } - return string(data) + return strings.TrimSpace(string(data)) } diff --git a/cmii-uav-watchdog-agent/main.go b/cmii-uav-watchdog-agent/main.go deleted file mode 100644 index 458a1ce..0000000 --- a/cmii-uav-watchdog-agent/main.go +++ /dev/null @@ -1,69 +0,0 @@ -package main - -import ( - "cmii-uav-watchdog-agent/host_info" - "fmt" - "github.com/gin-gonic/gin" - "net/http" - "os" - "os/signal" - "syscall" -) - -func main() { - // 创建一个默认的 Gin 路由 - var r = gin.Default() // 定义一个 GET 路由 - r.GET("/ping", func(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{"message": "pong"}) - }) - - // 定义一个 POST 路由 - r.POST("/echo", func(c *gin.Context) { - var json map[string]interface{} - if err := c.ShouldBindJSON(&json); err == nil { - c.JSON(http.StatusOK, json) - } else { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - } - }) - - r.GET("/cpu", func(c *gin.Context) { - cpuInfo := services.GetCPUInfo() // 直接返回 CPU 信息 - c.JSON(http.StatusOK, cpuInfo) - }) - - r.GET("/memory", func(c *gin.Context) { - memInfo := services.GetMemoryInfo() // 直接返回内存信息 - c.JSON(http.StatusOK, memInfo) - }) - - r.GET("/disk", func(c *gin.Context) { - diskInfo := services.GetDiskInfo() // 直接返回磁盘信息 - c.JSON(http.StatusOK, diskInfo) - }) - - r.GET("/motherboard", func(c *gin.Context) { - mbInfo := services.GetMotherboardInfo() // 直接返回主板信息 - c.JSON(http.StatusOK, mbInfo) - }) - - r.GET("/network", func(c *gin.Context) { - networkInterfaces := services.GetNetworkInterfaces() - c.JSON(http.StatusOK, networkInterfaces) - }) - r.GET("/all", func(c *gin.Context) { - allInfo := services.GetAllInfo() - c.JSON(http.StatusOK, allInfo) - }) - //r.GET("/phy", func(c *gin.Context) { - // allInfo, _ := services.GetPVForLV() - // c.JSON(http.StatusOK, allInfo) - //}) - // 启动服务,监听在 8080 端口 - r.Run(":8098") - // 等待终止信号 - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) - <-sigs - fmt.Println("Shutting down service...") -} diff --git a/cmii-uav-watchdog-agent/rpc/rpc.go b/cmii-uav-watchdog-agent/rpc/rpc.go index 9eac94e..c722711 100644 --- a/cmii-uav-watchdog-agent/rpc/rpc.go +++ b/cmii-uav-watchdog-agent/rpc/rpc.go @@ -8,6 +8,7 @@ import ( "fmt" "io" "net/http" + "strings" "time" "cmii-uav-watchdog-common/models" @@ -23,7 +24,8 @@ var ( ) const ( - DefaultHeartbeatURL = "http://cmii-uav-watchdog/heartbeat" + DefaultHeartbeatURL = "http://cmii-uav-watchdog:8080/api/heartbeat" + // DefaultHeartbeatURL = "http://192.168.35.70:8990/api/heartbeat" ) // ClientOptions HTTP客户端配置选项 @@ -48,8 +50,9 @@ func DefaultClientOptions() *ClientOptions { // Client HTTP客户端封装 type Client struct { - httpClient *http.Client - options *ClientOptions + httpClient *http.Client + options *ClientOptions + heartbeatURL string // 心跳请求的URL地址 } // NewClient 创建一个新的HTTP客户端 @@ -58,7 +61,7 @@ type Client struct { // // 返回: // - *Client: HTTP客户端实例 -func NewClient(options *ClientOptions) *Client { +func NewClient(options *ClientOptions, heartbeatURL string) *Client { if options == nil { options = DefaultClientOptions() } @@ -75,10 +78,22 @@ func NewClient(options *ClientOptions) *Client { Transport: transport, } - return &Client{ - httpClient: httpClient, - options: options, + // 解析heartbeatURL + heartbeatURL = strings.TrimSpace(heartbeatURL) + if heartbeatURL == "" { + heartbeatURL = DefaultHeartbeatURL } + + return &Client{ + httpClient: httpClient, + options: options, + heartbeatURL: heartbeatURL, + } +} + +// GetHeartbeatURL 获取心跳请求的URL地址 +func (c *Client) GetHeartbeatURL() string { + return c.heartbeatURL } // SendHeartbeat 发送心跳请求并处理响应 @@ -114,7 +129,7 @@ func (c *Client) SendHeartbeat(ctx context.Context, request *models.HeartbeatReq } // 创建HTTP请求 - req, err := http.NewRequestWithContext(ctx, http.MethodPost, DefaultHeartbeatURL, bytes.NewBuffer(requestBody)) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.heartbeatURL, bytes.NewBuffer(requestBody)) if err != nil { lastError = fmt.Errorf("创建请求失败: %w", err) continue @@ -140,9 +155,37 @@ func (c *Client) SendHeartbeat(ctx context.Context, request *models.HeartbeatReq // 确保响应体被关闭 defer resp.Body.Close() - // 检查HTTP状态码 - if resp.StatusCode != http.StatusOK { - lastError = fmt.Errorf("%w: 状态码 %d", ErrInvalidStatusCode, resp.StatusCode) + // 使用switch检查HTTP状态码 + switch resp.StatusCode { + case http.StatusOK: + // 状态码为200,继续处理响应 + case http.StatusBadRequest: + lastError = fmt.Errorf("%w: 状态码 %d,请求无效", ErrInvalidStatusCode, resp.StatusCode) + // 读取并丢弃响应体,避免连接泄漏 + _, _ = io.Copy(io.Discard, resp.Body) + continue + case http.StatusUnauthorized: + lastError = fmt.Errorf("%w: 状态码 %d,未授权", ErrInvalidStatusCode, resp.StatusCode) + // 读取并丢弃响应体,避免连接泄漏 + _, _ = io.Copy(io.Discard, resp.Body) + continue + case http.StatusForbidden: + lastError = fmt.Errorf("%w: 状态码 %d,禁止访问", ErrInvalidStatusCode, resp.StatusCode) + // 读取并丢弃响应体,避免连接泄漏 + _, _ = io.Copy(io.Discard, resp.Body) + continue + case http.StatusNotFound: + lastError = fmt.Errorf("%w: 状态码 %d,未找到资源", ErrInvalidStatusCode, resp.StatusCode) + // 读取并丢弃响应体,避免连接泄漏 + _, _ = io.Copy(io.Discard, resp.Body) + continue + case http.StatusInternalServerError: + lastError = fmt.Errorf("%w: 状态码 %d,服务器内部错误", ErrInvalidStatusCode, resp.StatusCode) + // 读取并丢弃响应体,避免连接泄漏 + _, _ = io.Copy(io.Discard, resp.Body) + continue + default: + lastError = fmt.Errorf("%w: 状态码 %d,未知错误", ErrInvalidStatusCode, resp.StatusCode) // 读取并丢弃响应体,避免连接泄漏 _, _ = io.Copy(io.Discard, resp.Body) continue diff --git a/cmii-uav-watchdog-agent/totp/auth.go b/cmii-uav-watchdog-agent/totp/auth.go deleted file mode 100644 index 205fc4f..0000000 --- a/cmii-uav-watchdog-agent/totp/auth.go +++ /dev/null @@ -1,105 +0,0 @@ -package totp - -import ( - "crypto/hmac" - "crypto/sha1" - "encoding/base32" - "encoding/binary" - "fmt" - "strings" - "sync" - "time" -) - -// TOTPConfig TOTP配置 -type TOTPConfig struct { - Secret string // TOTP密钥 - Digits int // TOTP验证码长度 - TimeStep time.Duration // TOTP时间步长 - Algorithm string // TOTP算法 -} - -var ( - defaultConfig = TOTPConfig{ - Secret: "", - Digits: 6, - TimeStep: 30 * time.Second, - Algorithm: "SHA1", - } - mu sync.RWMutex -) - -// SetTOTPSecret 设置TOTP密钥 -func SetTOTPSecret(secret string) { - mu.Lock() - defer mu.Unlock() - defaultConfig.Secret = secret -} - -// GetTOTPSecret 获取TOTP密钥 -func GetTOTPSecret() string { - mu.RLock() - defer mu.RUnlock() - return defaultConfig.Secret -} - -// GenerateTOTPCode 生成TOTP验证码 -func GenerateTOTPCode() (string, error) { - mu.RLock() - config := defaultConfig - mu.RUnlock() - - if config.Secret == "" { - return "", fmt.Errorf("TOTP密钥未设置") - } - - // 确保密钥是Base32编码的 - secret := strings.ToUpper(config.Secret) - secret = strings.ReplaceAll(secret, " ", "") - secretBytes, err := base32.StdEncoding.DecodeString(secret) - if err != nil { - return "", fmt.Errorf("解码TOTP密钥失败: %w", err) - } - - // 获取当前时间戳并转换为TOTP时间计数器 - timeCounter := uint64(time.Now().Unix()) / uint64(config.TimeStep.Seconds()) - - // 将时间计数器转换为字节数组 - timeBytes := make([]byte, 8) - binary.BigEndian.PutUint64(timeBytes, timeCounter) - - // 使用HMAC-SHA1计算TOTP值 - h := hmac.New(sha1.New, secretBytes) - h.Write(timeBytes) - hash := h.Sum(nil) - - // 根据RFC 6238,我们使用哈希的最后一个字节的低4位作为偏移 - offset := hash[len(hash)-1] & 0x0f - - // 从哈希中提取4字节并转换为整数 - binary := binary.BigEndian.Uint32(hash[offset : offset+4]) - - // 屏蔽最高位并获取指定位数的数字 - totp := binary & 0x7fffffff % uint32(pow10(config.Digits)) - - // 将数字格式化为字符串,填充前导零 - return fmt.Sprintf("%0*d", config.Digits, totp), nil -} - -// ValidateTOTPCode 验证TOTP验证码 -func ValidateTOTPCode(code string) bool { - generatedCode, err := GenerateTOTPCode() - if err != nil { - return false - } - return code == generatedCode -} - -// pow10 计算10的n次方 -func pow10(n int) int { - result := 1 - for i := 0; i < n; i++ { - result *= 10 - } - return result -} diff --git a/cmii-uav-watchdog-center/cmd/main.go b/cmii-uav-watchdog-center/cmd/main.go new file mode 100644 index 0000000..eef1ba7 --- /dev/null +++ b/cmii-uav-watchdog-center/cmd/main.go @@ -0,0 +1,37 @@ +package main + +import ( + "cmii-uav-watchdog-center/config" + "cmii-uav-watchdog-center/router" + "cmii-uav-watchdog-common/wdd_log" + "os" + "path/filepath" + "strconv" +) + +func main() { + // 获取执行目录 + execDir, err := filepath.Abs(filepath.Dir(os.Args[0])) + if err != nil { + wdd_log.Fatal("获取执行目录失败: %v", err) + } + + // 配置文件路径 + configPath := filepath.Join(execDir, "config.json") + + // 加载配置 + config, err := config.LoadConfig(configPath) + if err != nil { + wdd_log.Fatal("加载配置失败: %v", err) + } + + // 设置路由 + r := router.SetupRouter() + + // 启动服务器 + port := config.Server.Port + wdd_log.Info("服务器正在监听端口: %d", port) + if err := r.Run(":" + strconv.Itoa(port)); err != nil { + wdd_log.Error("启动服务器失败: %v", err) + } +} diff --git a/cmii-uav-watchdog-center/config/config.go b/cmii-uav-watchdog-center/config/config.go new file mode 100644 index 0000000..376ffd5 --- /dev/null +++ b/cmii-uav-watchdog-center/config/config.go @@ -0,0 +1,11 @@ +package config + +// Config 配置模型 +type Config struct { + Server ServerConfig `json:"server"` // 服务器配置 +} + +// ServerConfig 服务器配置 +type ServerConfig struct { + Port int `json:"port"` // 服务器端口 +} diff --git a/cmii-uav-watchdog-center/config/config_loader.go b/cmii-uav-watchdog-center/config/config_loader.go new file mode 100644 index 0000000..1201901 --- /dev/null +++ b/cmii-uav-watchdog-center/config/config_loader.go @@ -0,0 +1,74 @@ +package config + +import ( + "cmii-uav-watchdog-common/wdd_log" + "encoding/json" + "os" + "sync" +) + +var ( + config *Config + configOnce sync.Once +) + +// LoadConfig 加载配置 +func LoadConfig(configPath string) (*Config, error) { + configOnce.Do(func() { + // 读取配置文件 + data, err := os.ReadFile(configPath) + if err != nil { + // 如果配置文件不存在,则创建默认配置 + if os.IsNotExist(err) { + config = createDefaultConfig() + // 保存默认配置 + saveConfig(configPath, config) + return + } + wdd_log.Error("读取配置文件失败: %v", err) + return + } + + // 解析配置 + var cfg Config + if err := json.Unmarshal(data, &cfg); err != nil { + wdd_log.Error("解析配置文件失败: %v", err) + return + } + + config = &cfg + }) + + return config, nil +} + +// GetConfig 获取配置 +func GetConfig() *Config { + if config == nil { + // 如果配置未初始化,则加载默认配置 + config = createDefaultConfig() + } + return config +} + +// 创建默认配置 +func createDefaultConfig() *Config { + return &Config{ + Server: ServerConfig{ + Port: 8080, + }, + } +} + +// 保存配置 +func saveConfig(configPath string, cfg *Config) { + data, err := json.MarshalIndent(cfg, "", " ") + if err != nil { + wdd_log.Error("序列化配置失败: %v", err) + return + } + + if err := os.WriteFile(configPath, data, 0644); err != nil { + wdd_log.Error("保存配置文件失败: %v", err) + } +} diff --git a/cmii-uav-watchdog-center/controllers/auth_controller.go b/cmii-uav-watchdog-center/controllers/auth_controller.go new file mode 100644 index 0000000..6fb8549 --- /dev/null +++ b/cmii-uav-watchdog-center/controllers/auth_controller.go @@ -0,0 +1,38 @@ +package controllers + +import ( + "cmii-uav-watchdog-center/services" + "cmii-uav-watchdog-common/models" + "net/http" + + "github.com/gin-gonic/gin" +) + +// ProcessAuthorizationFile 处理授权文件 +// @Summary 处理授权文件 +// @Description 接收来自cmii-uav-watchdog的授权文件,解码并生成授权码 +// @Accept json +// @Produce json +// @Param authFile body models.AuthorizationFile true "授权文件" +// @Success 200 {object} models.AuthorizationCode "授权码" +// @Failure 400 {object} map[string]string "请求错误" +// @Failure 401 {object} map[string]string "未授权" +// @Router /api/v1/auth/process [post] +func ProcessAuthorizationFile(c *gin.Context) { + var authFile models.AuthorizationFile + + // 解析请求体 + if err := c.ShouldBindJSON(&authFile); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求体"}) + return + } + + authCode, err := services.ProcessAuthorizationFile(authFile) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "生成授权码失败"}) + return + } + + // 返回授权码 + c.JSON(http.StatusOK, authCode) +} diff --git a/cmii-uav-watchdog-center/controllers/project_controller.go b/cmii-uav-watchdog-center/controllers/project_controller.go new file mode 100644 index 0000000..c425306 --- /dev/null +++ b/cmii-uav-watchdog-center/controllers/project_controller.go @@ -0,0 +1,45 @@ +package controllers + +import ( + "cmii-uav-watchdog-center/services" + "cmii-uav-watchdog-common/models" + "cmii-uav-watchdog-common/wdd_log" + "net/http" + + "github.com/gin-gonic/gin" +) + +// GetProjectList 获取项目列表 +func GetProjectList(c *gin.Context) { + + // 获取项目列表 + projectList, err := services.GetProjectList() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, projectList) + +} + +// CreateProject 创建项目 +func CreateProject(c *gin.Context) { + // 绑定 Project + var project models.Project + if err := c.ShouldBindJSON(&project); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // 创建项目 + authFilePath, err := services.CreateProject(&project) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + wdd_log.Info("创建项目成功: %s", authFilePath) + + c.JSON(http.StatusOK, project) +} diff --git a/cmii-uav-watchdog-center/go.mod b/cmii-uav-watchdog-center/go.mod index 71a4363..7266c73 100644 --- a/cmii-uav-watchdog-center/go.mod +++ b/cmii-uav-watchdog-center/go.mod @@ -1,3 +1,40 @@ module cmii-uav-watchdog-center -go 1.23 +go 1.24 + +require ( + cmii-uav-watchdog-common v0.0.0 + cmii-uav-watchdog-otp v0.0.0 + github.com/gin-gonic/gin v1.9.1 +) + +require ( + github.com/bytedance/sonic v1.9.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace cmii-uav-watchdog-common => ../cmii-uav-watchdog-common + +replace cmii-uav-watchdog-otp => ../cmii-uav-watchdog-otp diff --git a/cmii-uav-watchdog-center/go.sum b/cmii-uav-watchdog-center/go.sum new file mode 100644 index 0000000..1a77fa1 --- /dev/null +++ b/cmii-uav-watchdog-center/go.sum @@ -0,0 +1,86 @@ +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/cmii-uav-watchdog-center/router/router.go b/cmii-uav-watchdog-center/router/router.go new file mode 100644 index 0000000..84e220b --- /dev/null +++ b/cmii-uav-watchdog-center/router/router.go @@ -0,0 +1,36 @@ +package router + +import ( + "cmii-uav-watchdog-center/controllers" + + "github.com/gin-gonic/gin" +) + +// SetupRouter 设置路由 +func SetupRouter() *gin.Engine { + // 创建一个默认的路由引擎 + r := gin.Default() + + // API路由组 + apiV1 := r.Group("/api") + { + // 授权相关路由 + auth := apiV1.Group("/auth") + { + // 处理授权文件 + auth.POST("/process", controllers.ProcessAuthorizationFile) + } + + // 项目相关路由 + project := apiV1.Group("/project") + { + // 获取项目列表 + project.GET("/list", controllers.GetProjectList) + // 创建项目 + project.POST("/create", controllers.CreateProject) + + } + } + + return r +} diff --git a/cmii-uav-watchdog-center/services/auth_service.go b/cmii-uav-watchdog-center/services/auth_service.go new file mode 100644 index 0000000..43d025f --- /dev/null +++ b/cmii-uav-watchdog-center/services/auth_service.go @@ -0,0 +1,64 @@ +package services + +import ( + "cmii-uav-watchdog-common/models" + "cmii-uav-watchdog-common/totp_tier_one" + "cmii-uav-watchdog-common/utils" + "cmii-uav-watchdog-common/wdd_log" +) + +func ProcessAuthorizationFile(authFile models.AuthorizationFile) (models.AuthorizationCode, error) { + // 解密项目NameSpace + projectNamespace, err := totp_tier_one.Decrypt(authFile.EncryptedNamespace, authFile.ProjectNamespace) + if err != nil { + wdd_log.Error("解密项目Name失败: %v", err) + return models.AuthorizationCode{}, err + } + + // 获取项目信息 + projectInfo, err := GetProjectInfo(projectNamespace) + if err != nil { + wdd_log.Error("获取项目信息失败: %v", err) + return models.AuthorizationCode{}, err + } + + // 验证TOTP验证码 + if !totp_tier_one.VerifyTierOneTOTPCode(authFile.TOTPCode, projectInfo.TierOneSecret) { + wdd_log.Warn("TOTP验证失败: %s", authFile.TOTPCode) + return models.AuthorizationCode{}, err + } + + // 处理加密的主机信息 + + // 一级TOTP密钥 + key := projectInfo.TierOneSecret + + // 注意:由于我们无法解密主机信息(我们只需要验证其有效性),所以直接使用 + authorizedHostMap := make(map[string]models.HostInfo) + for encryptedInfo, hostInfo := range authFile.EncryptedHostMap { + // 使用 DecryptHostInfo 验证主机信息是否被篡改 + isOK, err := totp_tier_one.DecryptHostInfo(encryptedInfo, hostInfo, key) + if !isOK || err != nil { + wdd_log.Error("主机信息验证失败: %v", err) + continue + } + + // 验证通过,将主机信息添加到授权列表 + authorizedHostMap[encryptedInfo] = hostInfo + } + + // 生成新的TOTP验证码 + newTOTPCode, err := totp_tier_one.GenerateTierOneTOTPCode(projectInfo.TierOneSecret) + if err != nil { + wdd_log.Error("生成TOTP验证码失败: %v", err) + return models.AuthorizationCode{}, err + } + + return models.AuthorizationCode{ + TOTPCode: newTOTPCode, + CurrentTime: utils.CurentTimeString(), + EncryptedHostMap: authorizedHostMap, + ProjectNamespace: projectNamespace, + EncryptedNamespace: authFile.EncryptedNamespace, + }, nil +} diff --git a/cmii-uav-watchdog-center/services/project_service.go b/cmii-uav-watchdog-center/services/project_service.go new file mode 100644 index 0000000..2fd9f9e --- /dev/null +++ b/cmii-uav-watchdog-center/services/project_service.go @@ -0,0 +1,101 @@ +package services + +import ( + "cmii-uav-watchdog-common/models" + "cmii-uav-watchdog-common/utils" + "cmii-uav-watchdog-common/wdd_log" + "encoding/json" + "fmt" + "os" +) + +const ( + ProjectFilePath = "C:\\Users\\wddsh\\Documents\\IdeaProjects\\cmii-uav-watchdog-project\\cmii-uav-watchdog-center\\project_file\\" +) + +func CreateProject(project *models.Project) (string, error) { + + // 生成TierOneSecret + secret, err := GenerateTierOneTOTPSecret() + if err != nil { + wdd_log.Error("生成TierOneSecret失败: %v", err) + return "", err + } + + project.TierOneSecret = secret + project.CreateTime = utils.CurentTimeString() + + // 存储 project到file中 + project.AuthFilePath = fmt.Sprintf("%s%s-%s.json", ProjectFilePath, project.Namespace, project.Name) + + // 存储到file中 + jsonData, err := json.Marshal(project) + if err != nil { + wdd_log.Error("序列化失败: %v", err) + return "", err + } + + // 写入文件 + err = os.WriteFile(project.AuthFilePath, jsonData, 0644) + if err != nil { + wdd_log.Error("写入文件失败: %v", err) + return "", err + } + + return project.AuthFilePath, nil +} + +// GetProjectList 获取项目列表 +func GetProjectList() ([]models.Project, error) { + + // 获取项目列表 + projectFileList, err := os.ReadDir(ProjectFilePath) + if err != nil { + wdd_log.Error("读取项目列表失败: %v", err) + return nil, err + } + + projectList := make([]models.Project, 0) + + // 遍历项目列表 + for _, projectFilePath := range projectFileList { + // 读取项目文件 + projectFile, err := os.ReadFile(fmt.Sprintf("%s%s", ProjectFilePath, projectFilePath.Name())) + if err != nil { + wdd_log.Error("读取项目文件失败: %v", err) + return nil, err + } + + // 反序列化 + var project models.Project + err = json.Unmarshal(projectFile, &project) + if err != nil { + wdd_log.Error("反序列化失败: %v", err) + return nil, err + } + + // 添加到项目列表 + projectList = append(projectList, project) + } + + return projectList, nil +} + +// GetProjectInfo 获取项目信息 +func GetProjectInfo(projectNamespace string) (models.Project, error) { + // 获取项目列表 + projectList, err := GetProjectList() + if err != nil { + wdd_log.Error("获取项目列表失败: %v", err) + return models.Project{}, err + } + + // 遍历项目列表 + for _, project := range projectList { + if project.Namespace == projectNamespace { + return project, nil + } + } + + return models.Project{}, fmt.Errorf("项目不存在") +} diff --git a/cmii-uav-watchdog-center/services/totp_service.go b/cmii-uav-watchdog-center/services/totp_service.go new file mode 100644 index 0000000..f1034f6 --- /dev/null +++ b/cmii-uav-watchdog-center/services/totp_service.go @@ -0,0 +1,18 @@ +package services + +import ( + "cmii-uav-watchdog-common/totp_tier_one" + "cmii-uav-watchdog-otp/totp" + "log" +) + +// GenerateTierOneTOTPSecret 生成一级TOTP密钥 只能center调用 +func GenerateTierOneTOTPSecret() (string, error) { + secret, err := totp.Generate(totp_tier_one.TierOneTOTPSecretOpts) + if err != nil { + log.Printf("生成TOTP密钥失败: %v", err) + return "", err + } + + return secret.Secret(), nil +} diff --git a/cmii-uav-watchdog-common/go.mod b/cmii-uav-watchdog-common/go.mod index 503e87c..0549f23 100644 --- a/cmii-uav-watchdog-common/go.mod +++ b/cmii-uav-watchdog-common/go.mod @@ -1,3 +1,7 @@ module cmii-uav-watchdog-common go 1.24 + +require cmii-uav-watchdog-otp v0.0.0 + +replace cmii-uav-watchdog-otp => ../cmii-uav-watchdog-otp diff --git a/cmii-uav-watchdog-common/main.go b/cmii-uav-watchdog-common/main.go deleted file mode 100644 index 71ae5c9..0000000 --- a/cmii-uav-watchdog-common/main.go +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - "fmt" -) - -//TIP

To run your code, right-click the code and select Run.

Alternatively, click -// the icon in the gutter and select the Run menu item from here.

- -func main() { - //TIP

Press when your caret is at the underlined text - // to see how GoLand suggests fixing the warning.

Alternatively, if available, click the lightbulb to view possible fixes.

- s := "gopher" - fmt.Println("Hello and welcome, %s!", s) - - for i := 1; i <= 5; i++ { - //TIP

To start your debugging session, right-click your code in the editor and select the Debug option.

We have set one breakpoint - // for you, but you can always add more by pressing .

- fmt.Println("i =", 100/i) - } -} \ No newline at end of file diff --git a/cmii-uav-watchdog-common/models/between_project_model.go b/cmii-uav-watchdog-common/models/between_project_model.go index 4ac7791..6ec5744 100644 --- a/cmii-uav-watchdog-common/models/between_project_model.go +++ b/cmii-uav-watchdog-common/models/between_project_model.go @@ -1,28 +1,32 @@ package models -import "time" - // AuthorizationFile 授权文件模型 type AuthorizationFile struct { - EncryptedHosts []string `json:"encrypted_hosts"` // 加密后的主机信息列表 - TOTPCode string `json:"totp_code"` // TOTP验证码 - CurrentTime time.Time `json:"current_time"` // 当前系统时间 - FirstAuthTime time.Time `json:"first_auth_time"` // 初次授权时间 - TimeOffset int64 `json:"time_offset"` // 授权时间偏移 + EncryptedHostMap map[string]HostInfo `json:"encrypted_host_map"` // 加密后的主机信息列表 + TOTPCode string `json:"totp_code"` // TOTP验证码 + CurrentTime string `json:"current_time"` // 当前系统时间 + FirstAuthTime string `json:"first_auth_time"` // 初次授权时间 + TimeOffset int64 `json:"time_offset"` // 授权时间偏移 + ProjectNamespace string `json:"project_namespace"` // 项目命名空间 + EncryptedNamespace string `json:"encrypted_namespace"` // 加密后的项目命名空间 防止信息篡改 } // AuthorizationCode 授权码模型 type AuthorizationCode struct { - TOTPCode string `json:"totp_code"` // TOTP验证码 - CurrentTime time.Time `json:"current_time"` // 当前系统时间 - EncryptedHosts []string `json:"encrypted_hosts"` // 授权主机的加密字符串列表 + TOTPCode string `json:"totp_code"` // TOTP验证码 + CurrentTime string `json:"current_time"` // 当前系统时间 + EncryptedHostMap map[string]HostInfo `json:"encrypted_host_map"` // 加密后的主机信息列表 防止信息篡改 + ProjectNamespace string `json:"project_namespace"` // 项目命名空间 + EncryptedNamespace string `json:"encrypted_namespace"` // 加密后的项目命名空间 防止信息篡改 } // AuthorizationStorage 授权存储信息 type AuthorizationStorage struct { - EncryptedCode string `json:"encrypted_code"` // 加密后的授权码 - FirstAuthTime time.Time `json:"first_auth_time"` // 初次授权时间 - TimeOffset int64 `json:"time_offset"` // 授权时间偏移 - AuthorizedHosts []string `json:"authorized_hosts"` // 已授权主机列表 - SecondTOTPSecret string `json:"second_totp_secret"` // 第二级的totp密钥 + EncryptedAuthrizationCode string `json:"encrypted_authrization_code"` // 加密后的授权码,防止信息篡改 + FirstAuthTime string `json:"first_auth_time"` // 初次授权时间 + TimeOffset int64 `json:"time_offset"` // 授权时间偏移 + AuthorizedHostMap map[string]HostInfo `json:"authorized_host_map"` // 已授权主机列表 + SecondTOTPSecret string `json:"second_totp_secret"` // 第二级的totp密钥 + ProjectNamespace string `json:"project_namespace"` // 项目命名空间 + EncryptedNamespace string `json:"encrypted_namespace"` // 加密后的项目命名空间 防止信息篡改 } diff --git a/cmii-uav-watchdog-common/models/in_project_model.go b/cmii-uav-watchdog-common/models/in_project_model.go index 93e2d85..0b4e0d8 100644 --- a/cmii-uav-watchdog-common/models/in_project_model.go +++ b/cmii-uav-watchdog-common/models/in_project_model.go @@ -2,10 +2,13 @@ package models // CPUInfo 结构体用于存储 CPU 信息 type CPUInfo struct { - ModelName string `json:"model_name"` - Cores int `json:"cores"` - Architecture string `json:"architecture"` - UUID string `json:"uuid"` + ModelName string `json:"model_name"` + Cores int `json:"cores"` + Architecture string `json:"architecture"` + Flags []string `json:"flags"` + Hypervisor string `json:"hypervisor"` + Virtualization string `json:"virtualization"` + Frequency string `json:"frequency"` } // MemoryInfo holds the memory information. @@ -49,22 +52,21 @@ type MotherboardInfo struct { } type SystemInfo struct { - MachineID string `json:"machine_id"` // 唯一标识符 + MachineID string `json:"machine_id"` // 主机唯一标识符 + MachineSerial string `json:"machine_serial"` // 主机序列号 OS OSInfo `json:"os"` // 操作系统 KernelVersion string `json:"kernel_version"` // 内核版本 + NodeName string `json:"node_name"` // 节点名称 + NodeIP string `json:"node_ip"` // 节点IP } type OSInfo struct { - Name string `json:"name"` - Version string `json:"version"` - ID string `json:"id"` - IDLike string `json:"id_like"` - VersionID string `json:"version_id"` - PrettyName string `json:"pretty_name"` - HomeURL string `json:"home_url"` - SupportURL string `json:"support_url"` - BugReportURL string `json:"bug_report_url"` - PrivacyURL string `json:"privacy_url"` + Name string `json:"name"` + Version string `json:"version"` + ID string `json:"id"` + IDLike string `json:"id_like"` + VersionID string `json:"version_id"` + PrettyName string `json:"pretty_name"` } // HostInfo 主机信息模型 @@ -77,12 +79,32 @@ type HostInfo struct { MotherboardInfo MotherboardInfo `json:"motherboard_info"` } +// EnvInfo 环境信息 +type EnvInfo struct { + K8S_NAMESPACE string `json:"k8s_namespace"` // 环境名称 + APPLICATION_NAME string `json:"application_name"` // 应用名称 + CUST_JAVA_OPTS string `json:"cust_java_opts"` // 自定义java参数 + BIZ_CONFIG_GROUP string `json:"biz_config_group"` // 业务配置组 + SYS_CONFIG_GROUP string `json:"sys_config_group"` // 系统配置组 + IMAGE_NAME string `json:"image_name"` // 镜像名称 + JAVA_VERSION string `json:"java_version"` // java版本 + GIT_COMMIT string `json:"git_commit"` // git commit + GIT_BRANCH string `json:"git_branch"` // git branch + NODE_NAME string `json:"node_name"` // 节点名称 + NODE_IP string `json:"node_ip"` // 节点ip + POD_NAME string `json:"pod_name"` // pod名称 + LIMIT_CPU string `json:"limit_cpu"` // 限制cpu + LIMIT_MEMORY string `json:"limit_memory"` // 限制内存 + REQUEST_CPU string `json:"request_cpu"` // 请求cpu + REQUEST_MEMORY string `json:"request_memory"` // 请求内存 +} + // HeartbeatRequest 心跳请求 type HeartbeatRequest struct { HostInfo HostInfo `json:"host_info"` // 主机信息 + EnvInfo EnvInfo `json:"env_info"` // 环境信息 Timestamp int64 `json:"timestamp"` // 时间戳 TOTPCode string `json:"totp_code"` // TOTP验证码 - AppName string `json:"app_name"` // 应用名称 } // HeartbeatResponse 心跳响应 diff --git a/cmii-uav-watchdog-common/models/project.go b/cmii-uav-watchdog-common/models/project.go new file mode 100644 index 0000000..8a37f4e --- /dev/null +++ b/cmii-uav-watchdog-common/models/project.go @@ -0,0 +1,22 @@ +package models + +// Project 项目信息 +type Project struct { + Name string `json:"name" binding:"required"` + Namespace string `json:"namespace" binding:"required"` + Province string `json:"province"` + City string `json:"city"` + Domain string `json:"domain"` + BusinessMan string `json:"business_man"` + MarketMan string `json:"market_man" ` + TierOneSecret string `json:"tier_one_secret" ` + TierTwoSecret string `json:"tier_two_secret"` + AuthTime string `json:"auth_time"` + AuthFilePath string `json:"auth_file_path"` + CreateTime string `json:"create_time"` +} + +// ProjectPO 项目持久化对象 +type ProjectPO struct { + Project +} diff --git a/cmii-uav-watchdog-common/totp_tier_one/crypto.go b/cmii-uav-watchdog-common/totp_tier_one/crypto.go new file mode 100644 index 0000000..3dfcbc3 --- /dev/null +++ b/cmii-uav-watchdog-common/totp_tier_one/crypto.go @@ -0,0 +1,169 @@ +package totp_tier_one + +import ( + "cmii-uav-watchdog-common/models" + "cmii-uav-watchdog-common/wdd_log" + "crypto/aes" + "crypto/cipher" + "crypto/sha256" + "encoding/base64" + "errors" + "fmt" + "strconv" + "strings" +) + +// Encrypt 加密字符串 +// 保证使用相同的plaintext和key进行加密,在不同时间、不同机器上输出的加密字符串都是固定的 +func Encrypt(plaintext string, key string) (encryptedPlaintext string, encryptError error) { + // 创建hash + hasher := sha256.New() + hasher.Write([]byte(key)) + keyBytes := hasher.Sum(nil) + + // 创建cipher + block, err := aes.NewCipher(keyBytes) + if err != nil { + return "", err + } + + // 创建gcm + gcm, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + + // 生成确定性nonce - 使用plaintext和key的组合生成,确保对相同输入产生相同nonce + nonceHasher := sha256.New() + nonceHasher.Write([]byte(key + "fixed_salt_for_deterministic_result" + plaintext[:min(10, len(plaintext))])) + nonceSource := nonceHasher.Sum(nil) + + // 创建合适大小的nonce + nonce := nonceSource[:gcm.NonceSize()] + + // 加密 + ciphertext := gcm.Seal(nil, nonce, []byte(plaintext), nil) + + // 将nonce和密文连接起来 + result := append(nonce, ciphertext...) + + // 编码为base64 + return base64.StdEncoding.EncodeToString(result), nil +} + +// Decrypt 解密字符串 +// 可以使用加密字符串和key在不同时间、不同机器上解密出原始plaintext +func Decrypt(encrypted string, key string) (decryptedPlaintext string, decryptError error) { + // 解码base64 + data, err := base64.StdEncoding.DecodeString(encrypted) + if err != nil { + return "", err + } + + // 创建hash + hasher := sha256.New() + hasher.Write([]byte(key)) + keyBytes := hasher.Sum(nil) + + // 创建cipher + block, err := aes.NewCipher(keyBytes) + if err != nil { + return "", err + } + + // 创建gcm + gcm, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + + // 检查长度 + if len(data) < gcm.NonceSize() { + return "", errors.New("密文太短") + } + + // 分离nonce和密文 + nonce, ciphertext := data[:gcm.NonceSize()], data[gcm.NonceSize():] + + // 解密 + plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) + if err != nil { + return "", err + } + + return string(plaintext), nil +} + +// min 返回两个整数中较小的一个 +func min(a, b int) int { + if a < b { + return a + } + return b +} + +// EncryptHostInfo 加密主机信息 +func EncryptHostInfo(hostInfo models.HostInfo, key string) (encryptedHostInfo string, encryptError error) { + + // 生成主机唯一标识作为加密密钥 + info := generateHostUniqueID(hostInfo) + + // 加密主机信息 + encrypted, err := Encrypt(info, key) + if err != nil { + return "", err + } + + return encrypted, nil +} + +// DecryptHostInfo 解密主机信息 +func DecryptHostInfo(encrypted string, hostInfo models.HostInfo, key string) (decryptedOK bool, decryptError error) { + + // 解密主机信息 + decryptedStr, err := Decrypt(encrypted, key) + if err != nil { + wdd_log.Error("[DecryptHostInfo] - Decrypt失败: %v", err) + return false, err + } + + // wdd_log.Info("[DecryptHostInfo] - Decrypt成功: %s", decryptedStr) + + // 生成主机唯一标识作为加密密钥 + hostUniqueID := generateHostUniqueID(hostInfo) + if hostUniqueID != decryptedStr { + wdd_log.Error("[DecryptHostInfo] - 主机信息篡改") + return false, errors.New("主机信息篡改") + } + + return true, nil +} + +// generateHostUniqueID 生成主机唯一标识作为加密密钥 +func generateHostUniqueID(hostInfo models.HostInfo) string { + // hostinfo.SystemInfo.MachineID + machineID := hostInfo.SystemInfo.MachineID + + // hostinfo.SystemInfo.KernelVersion + kernelVersion := hostInfo.SystemInfo.KernelVersion + + // 将CPU信息中的ModelName Cores Hypervisor 拼接起来 + cpuCores := hostInfo.CPUInfo.Cores + cpuHypervisor := hostInfo.CPUInfo.Hypervisor + cpuModelName := hostInfo.CPUInfo.ModelName + + cpuInfo := fmt.Sprintf("%s-%d-%s", cpuModelName, cpuCores, cpuHypervisor) + + // 不能使用 mac 地址,因为 每个Pod 的 mac 地址会变化 + + // hostinfo.SystemInfo.MachineSerial + machineSerial := hostInfo.SystemInfo.MachineSerial + + // 将memory中的total 和 used 拼接起来 + memoryTotal := hostInfo.MemoryInfo.Total + + // 将上述的全部信息拼接起来 + info := strings.Join([]string{machineID, kernelVersion, cpuInfo, machineSerial, strconv.FormatUint(memoryTotal, 10)}, "==") + + return info +} diff --git a/cmii-uav-watchdog-common/totp_tier_one/crypto_test.go b/cmii-uav-watchdog-common/totp_tier_one/crypto_test.go new file mode 100644 index 0000000..db09f06 --- /dev/null +++ b/cmii-uav-watchdog-common/totp_tier_one/crypto_test.go @@ -0,0 +1,218 @@ +package totp_tier_one + +import ( + "cmii-uav-watchdog-common/models" + "fmt" + "testing" +) + +// TestEncryptDecrypt 测试加密和解密功能 +func TestEncryptDecrypt(t *testing.T) { + tests := []struct { + name string + plaintext string + key string + }{ + { + name: "正常文本加密解密", + plaintext: "这是一段测试文本", + key: "测试密钥", + }, + { + name: "空文本加密解密", + plaintext: "", + key: "测试密钥", + }, + { + name: "特殊字符加密解密", + plaintext: "!@#$%^&*()_+{}|:<>?", + key: "!@#$%^&*()_+{}|:<>?", + }, + { + name: "长文本加密解密", + plaintext: "这是一段非常长的测试文本,用于测试加密和解密功能是否能够正确处理长文本。这是一段非常长的测试文本,用于测试加密和解密功能是否能够正确处理长文本。", + key: "测试密钥", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 加密 + encrypted, err := Encrypt(tt.plaintext, tt.key) + if err != nil { + t.Fatalf("加密失败: %v", err) + } + + // 确保加密后的文本与原始文本不同 + if tt.plaintext != "" && encrypted == tt.plaintext { + t.Errorf("加密后的文本与原始文本相同,可能未正确加密") + } + + // 解密 + decrypted, err := Decrypt(encrypted, tt.key) + if err != nil { + t.Fatalf("解密失败: %v", err) + } + + // 确保解密后的文本与原始文本相同 + if decrypted != tt.plaintext { + t.Errorf("解密后的文本与原始文本不同,期望:%s,实际:%s", tt.plaintext, decrypted) + } + }) + } +} + +// TestDecryptWithWrongKey 测试使用错误密钥解密 +func TestDecryptWithWrongKey(t *testing.T) { + plaintext := "这是一段测试文本" + correctKey := "正确密钥" + wrongKey := "错误密钥" + + // 使用正确密钥加密 + encrypted, err := Encrypt(plaintext, correctKey) + if err != nil { + t.Fatalf("加密失败: %v", err) + } + + // 使用错误密钥解密,期望失败 + _, err = Decrypt(encrypted, wrongKey) + fmt.Println("解密失败的 error :", err) + if err == nil { + t.Errorf("使用错误密钥解密应该失败,但成功了") + } +} + +// TestDecryptInvalidData 测试解密无效的数据 +func TestDecryptInvalidData(t *testing.T) { + invalidEncrypted := "这不是有效的加密数据" + key := "测试密钥" + + _, err := Decrypt(invalidEncrypted, key) + if err == nil { + t.Errorf("解密无效数据应该失败,但成功了") + } +} + +// createMockHostInfo 创建模拟的主机信息用于测试 +func createMockHostInfo() models.HostInfo { + return models.HostInfo{ + SystemInfo: models.SystemInfo{ + MachineID: "test-machine-id", + KernelVersion: "5.10.0-test", + OS: models.OSInfo{ + Name: "TestOS", + Version: "1.0", + ID: "test", + IDLike: "test", + VersionID: "1.0", + PrettyName: "Test OS 1.0", + }, + }, + CPUInfo: models.CPUInfo{ + ModelName: "Test CPU", + Cores: 4, + Architecture: "x86_64", + Flags: []string{}, + Hypervisor: "", + Virtualization: "", + Frequency: "", + }, + DiskInfo: []models.DiskInfo{ + { + Device: "/dev/sda1", + Filesystem: "ext4", + Type: "ext4", + Size: 1000000, + Used: 500000, + Available: 500000, + UsePercent: "50%", + MountPoint: "/", + PhysicalDevice: "/dev/sda", + PhysicalSize: 2000000, + }, + }, + MemoryInfo: models.MemoryInfo{ + Total: 8000000, + Free: 4000000, + Available: 4000000, + Used: 4000000, + Buffers: 1000000, + Cached: 1000000, + Shared: 500000, + }, + NetInfo: []models.NetworkInterfaceInfo{ + { + Name: "eth0", + MACAddress: "00:11:22:33:44:55", + IPAddresses: []string{"192.168.1.100", "fe80::1234"}, + }, + }, + MotherboardInfo: models.MotherboardInfo{ + Manufacturer: "Test Manufacturer", + Product: "Test Product", + Version: "1.0", + Serial: "TEST123456", + }, + } +} + +// TestEncryptDecryptHostInfo 测试主机信息的加密和解密 +func TestEncryptDecryptHostInfo(t *testing.T) { + hostInfo := createMockHostInfo() + key := "test-key" + + // 加密主机信息 + encrypted, err := EncryptHostInfo(hostInfo, key) + fmt.Println("加密后的主机信息:", encrypted) + if err != nil { + t.Fatalf("加密主机信息失败: %v", err) + } + + // 确保加密后的文本不为空 + if encrypted == "" { + t.Errorf("加密后的主机信息为空") + } + + // 解密主机信息 + decrypted, err := DecryptHostInfo(encrypted, hostInfo, key) + if err != nil { + t.Fatalf("解密主机信息失败: %v", err) + } + + if !decrypted { + t.Errorf("解密后的主机信息与原始主机信息不同") + } +} + +// TestEncryptDecryptHostInfoWithModifiedInfo 测试使用修改过的主机信息解密 +func TestEncryptDecryptHostInfoWithModifiedInfo(t *testing.T) { + originalHostInfo := createMockHostInfo() + key := "test-key" + + // 加密主机信息 + encrypted, err := EncryptHostInfo(originalHostInfo, key) + if err != nil { + t.Fatalf("加密主机信息失败: %v", err) + } + + t.Logf("加密后的主机信息: %s", encrypted) + + // 创建修改过的主机信息 + modifiedHostInfo := originalHostInfo + + // 加密修改过的主机信息 + encryptedModified, err := EncryptHostInfo(modifiedHostInfo, key) + if err != nil { + t.Fatalf("加密修改过的主机信息失败: %v", err) + } + + t.Logf("加密修改后的主机信息: %s", encryptedModified) + + // 使用修改过的主机信息尝试解密,期望失败 + isOK, err := DecryptHostInfo(encryptedModified, originalHostInfo, key) + fmt.Println("解密后的主机信息 error :", err) + + if isOK { + t.Errorf("解密后的主机信息与修改后的主机信息相同, 不应该相同") + } +} diff --git a/cmii-uav-watchdog-common/totp_tier_one/totp_tier_one_service.go b/cmii-uav-watchdog-common/totp_tier_one/totp_tier_one_service.go new file mode 100644 index 0000000..1877ba0 --- /dev/null +++ b/cmii-uav-watchdog-common/totp_tier_one/totp_tier_one_service.go @@ -0,0 +1,39 @@ +package totp_tier_one + +import ( + otp "cmii-uav-watchdog-otp" + "cmii-uav-watchdog-otp/totp" + "time" +) + +var TierOneTOTPSecretOpts = totp.GenerateOpts{ + SecretSize: 64, + Issuer: "cmii-uav-watchdog-center", + AccountName: "cmii-uav-watchdog-center", + Period: 30 * 2 * 30, // 30分钟 + Digits: otp.DigitsEight, + Algorithm: otp.AlgorithmSHA256, +} + +// GenerateTierOneTOTPCode 生成一级TOTP验证码 +func GenerateTierOneTOTPCode(secret string) (string, error) { + validateOpts := totp.ValidateOpts{} + validateOpts.ConvertToValidateOpts(TierOneTOTPSecretOpts) + + code, err := totp.GenerateCodeCustom(secret, time.Now(), validateOpts) + if err != nil { + return "", err + } + return code, nil +} + +// VerifyTierOneTOTPCode 验证一级TOTP验证码 +func VerifyTierOneTOTPCode(code string, secret string) bool { + validateOpts := totp.ValidateOpts{} + validateOpts.ConvertToValidateOpts(TierOneTOTPSecretOpts) + valid, err := totp.ValidateCustom(code, secret, time.Now(), validateOpts) + if err != nil { + return false + } + return valid +} diff --git a/cmii-uav-watchdog-common/totp_tier_two/totp_tier_two_service.go b/cmii-uav-watchdog-common/totp_tier_two/totp_tier_two_service.go new file mode 100644 index 0000000..9bfbc5e --- /dev/null +++ b/cmii-uav-watchdog-common/totp_tier_two/totp_tier_two_service.go @@ -0,0 +1,54 @@ +package totp_tier_two + +import ( + "cmii-uav-watchdog-common/utils" + "cmii-uav-watchdog-common/wdd_log" + otp "cmii-uav-watchdog-otp" + "cmii-uav-watchdog-otp/totp" +) + +var TierTwoTOTPSecretOpts = totp.GenerateOpts{ + SecretSize: 32, + Issuer: "cmii-uav-watchdog", + AccountName: "cmii-uav-watchdog", + Period: 30, + Secret: []byte{}, + Digits: otp.DigitsSix, + Algorithm: otp.AlgorithmSHA1, + Rand: nil, +} + +// GenerateTierTwoTOTPSecret 生成二级TOTP密钥 +func GenerateTierTwoTOTPSecret() (string, error) { + secret, err := totp.Generate(TierTwoTOTPSecretOpts) + if err != nil { + wdd_log.Error("生成TOTP密钥失败: %v", err) + return "", err + } + wdd_log.Info("生成TOTP密钥成功: %s", secret.Secret()) + return secret.Secret(), nil +} + +// GenerateTierTwoTOTPCode 生成二级TOTP验证码 +func GenerateTierTwoTOTPCode(secret string) (string, error) { + validateOpts := totp.ValidateOpts{} + validateOpts.ConvertToValidateOpts(TierTwoTOTPSecretOpts) + code, err := totp.GenerateCodeCustom(secret, utils.CurentTime(), validateOpts) + if err != nil { + wdd_log.Error("TierTwo TOTP验证码生成失败: %v", err) + return "", err + } + return code, nil +} + +// VerifyTierTwoTOTPCode 验证二级TOTP验证码 +func VerifyTierTwoTOTPCode(code string, secret string) bool { + validateOpts := totp.ValidateOpts{} + validateOpts.ConvertToValidateOpts(TierTwoTOTPSecretOpts) + valid, err := totp.ValidateCustom(code, secret, utils.CurentTime(), validateOpts) + if err != nil { + wdd_log.Error("TierTwo TOTP验证失败: %v", err) + return false + } + return valid +} diff --git a/cmii-uav-watchdog-common/utils/time_utils.go b/cmii-uav-watchdog-common/utils/time_utils.go new file mode 100644 index 0000000..aab4e1f --- /dev/null +++ b/cmii-uav-watchdog-common/utils/time_utils.go @@ -0,0 +1,36 @@ +package utils + +import ( + "time" +) + +var CST = time.FixedZone("CST", 8*60*60) + +// CurentTimeString 获取当前时间字符串 东八区时间 +func CurentTimeString() string { + + return time.Now().In(CST).Format("2006-01-02 15:04:05") +} + +// CurentTime 获取当前时间 东八区时间 +func CurentTime() time.Time { + + return time.Now().In(CST) +} + +// CurentTimeUnix 获取当前时间戳 东八区时间 +func CurentTimeUnix() int64 { + return CurentTime().Unix() +} + +// ParseTimeString 解析时间字符串 东八区时间 +func ParseTimeString(timeString string) (time.Time, error) { + + return time.ParseInLocation("2006-01-02 15:04:05", timeString, CST) +} + +// ParseTimeUnix 解析时间戳 东八区时间 +func ParseTimeUnix(unix int64) (time.Time, error) { + + return time.Unix(unix, 0).In(CST), nil +} diff --git a/cmii-uav-watchdog-common/wdd_log/log_utils.go b/cmii-uav-watchdog-common/wdd_log/log_utils.go new file mode 100644 index 0000000..d85b2a0 --- /dev/null +++ b/cmii-uav-watchdog-common/wdd_log/log_utils.go @@ -0,0 +1,150 @@ +package wdd_log + +import ( + "cmii-uav-watchdog-common/utils" + "fmt" + "sync" +) + +const ( + colorReset = "\033[0m" + colorRed = "\033[31m" + colorGreen = "\033[32m" + colorYellow = "\033[33m" + colorBlue = "\033[34m" + colorPurple = "\033[35m" + colorCyan = "\033[36m" + colorWhite = "\033[37m" +) + +type LogLevel int + +const ( + LevelDebug LogLevel = iota + LevelInfo + LevelWarn + LevelError + LevelFatal + LogPrefix = "cmii-uav-watchdog" +) + +var logLevelColors = map[LogLevel]string{ + LevelDebug: colorCyan, + LevelInfo: colorGreen, + LevelWarn: colorYellow, + LevelError: colorRed, + LevelFatal: colorPurple, +} + +var logLevelNames = map[LogLevel]string{ + LevelDebug: "DEBUG", + LevelInfo: "INFO", + LevelWarn: "WARN", + LevelError: "ERROR", + LevelFatal: "FATAL", +} + +// Logger 日志单例结构体 +type Logger struct { + // EnableDebug 是否启用Debug日志 + EnableDebug bool +} + +var ( + instance *Logger + once sync.Once +) + +// GetInstance 获取Logger单例实例 +// 返回Logger的单例对象指针 +func GetInstance() *Logger { + once.Do(func() { + instance = &Logger{ + EnableDebug: false, // 默认不启用Debug日志 + } + }) + return instance +} + +// SetEnableDebug 设置是否启用Debug日志 +// enable: true表示启用Debug日志,false表示禁用 +func (l *Logger) SetEnableDebug(enable bool) { + l.EnableDebug = enable +} + +// Log 记录指定级别的日志 +// level: 日志级别 +// format: 日志格式 +// args: 格式化参数 +func (l *Logger) Log(level LogLevel, format string, args ...interface{}) { + // Debug级别日志在未启用时不输出 + if level == LevelDebug && !l.EnableDebug { + return + } + + now := utils.CurentTimeString() + color := logLevelColors[level] + levelName := logLevelNames[level] + message := fmt.Sprintf(format, args...) + fmt.Printf("[%s] %s %s%s%s %s\n", LogPrefix, now, color, levelName, colorReset, message) +} + +// Debug 记录Debug级别日志 +// format: 日志格式 +// args: 格式化参数 +func (l *Logger) Debug(format string, args ...interface{}) { + l.Log(LevelDebug, format, args...) +} + +// Info 记录Info级别日志 +// format: 日志格式 +// args: 格式化参数 +func (l *Logger) Info(format string, args ...interface{}) { + l.Log(LevelInfo, format, args...) +} + +// Warn 记录Warn级别日志 +// format: 日志格式 +// args: 格式化参数 +func (l *Logger) Warn(format string, args ...interface{}) { + l.Log(LevelWarn, format, args...) +} + +// Error 记录Error级别日志 +// format: 日志格式 +// args: 格式化参数 +func (l *Logger) Error(format string, args ...interface{}) { + l.Log(LevelError, format, args...) +} + +// Fatal 记录Fatal级别日志 +// format: 日志格式 +// args: 格式化参数 +func (l *Logger) Fatal(format string, args ...interface{}) { + l.Log(LevelFatal, format, args...) +} + +// 为了兼容原有代码,保留全局函数,但内部调用单例实例 +func Log(level LogLevel, format string, args ...interface{}) { + GetInstance().Log(level, format, args...) +} + +func Debug(format string, args ...interface{}) { + GetInstance().Debug(format, args...) +} + +func Info(format string, args ...interface{}) { + GetInstance().Info(format, args...) +} + +func Warn(format string, args ...interface{}) { + GetInstance().Warn(format, args...) +} + +func Error(format string, args ...interface{}) { + GetInstance().Error(format, args...) +} + +func Fatal(format string, args ...interface{}) { + GetInstance().Fatal(format, args...) +} diff --git a/cmii-uav-watchdog-otp/hotp/hotp.go b/cmii-uav-watchdog-otp/hotp/hotp.go index 9f9ff19..1c32554 100644 --- a/cmii-uav-watchdog-otp/hotp/hotp.go +++ b/cmii-uav-watchdog-otp/hotp/hotp.go @@ -34,7 +34,7 @@ import ( "strings" ) -const debug = true +const debug = false // Validate a HOTP passcode given a counter and secret. // This is a shortcut for ValidateCustom, with parameters that diff --git a/cmii-uav-watchdog/config/config.go b/cmii-uav-watchdog/config/config.go index 0b43981..3f0d8ff 100644 --- a/cmii-uav-watchdog/config/config.go +++ b/cmii-uav-watchdog/config/config.go @@ -1,24 +1,42 @@ package config import ( + "cmii-uav-watchdog-common/wdd_log" + "os" "sync" + + "gopkg.in/yaml.v3" +) + +const ( + localAuthFilePath = "/cmii/cmii-uav-watchdog/auth_code.json" + configFilePath = "/cmii/cmii-uav-watchdog/config.yaml" ) // Config 配置结构体 type Config struct { Server struct { - Port string `mapstructure:"port"` + Port string `mapstructure:"port"` + Debug bool `mapstructure:"debug"` } `mapstructure:"server"` - Auth struct { - Secret string `mapstructure:"secret"` // TOTP密钥 - AuthFilePath string `mapstructure:"auth_file"` // 授权文件路径 - TimeOffsetAllowed int64 `mapstructure:"time_offset_allowed"` // 允许的时间偏移(秒) - } `mapstructure:"auth"` + TierOneAuth struct { + TierOneSecret string `mapstructure:"tier_one_secret" yaml:"tier_one_secret"` // TOTP密钥 + TimeOffsetAllowed int64 `mapstructure:"time_offset_allowed" yaml:"time_offset_allowed"` // 允许的时间偏移(秒) + LocalAuthFilePath string `mapstructure:"local_auth_file_path" yaml:"local_auth_file_path"` // 本地授权文件路径 + } `mapstructure:"tier_one_auth" yaml:"tier_one_auth"` WatchdogCenter struct { - URL string `mapstructure:"url"` // 一级授权中心地址 - } `mapstructure:"watchdog_center"` + URL string `mapstructure:"url" yaml:"url"` // 一级授权中心地址 + } `mapstructure:"watchdog_center" yaml:"watchdog_center"` + + Project struct { + ProjectNamespace string `mapstructure:"project_namespace" yaml:"project_namespace"` // 项目命名空间 + } `mapstructure:"project" yaml:"project"` + + TierTwoAuth struct { + TierTwoSecret string `mapstructure:"tier_two_secret" yaml:"tier_two_secret"` // 二级授权密钥 + } `mapstructure:"tier_two_auth" yaml:"tier_two_auth"` } var ( @@ -27,22 +45,56 @@ var ( ) // LoadConfig 加载配置 -func LoadConfig(path string) error { - once.Do(func() { - // 设置默认配置值 - config.Server.Port = "8080" - config.Auth.Secret = "default-secret-key-please-change-in-production" - config.Auth.AuthFilePath = "./auth-storage.json" - config.Auth.TimeOffsetAllowed = 3600 // 默认允许1小时的时间偏移 - config.WatchdogCenter.URL = "http://localhost:8081" +// 单例模式, 如果已经初始化过, 则直接返回 +// 如果config文件不存在,报错 无法启动 +// 不使用viper,用最简单的方式读取配置文件,解析其中的配置 +// 如果解析失败,无法得到config 则报错 无法启动 +func LoadConfig() error { + + var err error + once.Do(func() { + // 读取配置文件 + data, readErr := os.ReadFile(configFilePath) + if readErr != nil { + wdd_log.Fatal("无法读取配置文件: %v", readErr) + err = readErr + return + } + + // 解析配置文件 + parseErr := yaml.Unmarshal(data, &config) + if parseErr != nil { + wdd_log.Fatal("无法解析配置文件: %v", parseErr) + err = parseErr + return + } + + // 设置本地授权文件保存地点 + config.TierOneAuth.LocalAuthFilePath = localAuthFilePath + wdd_log.Info("[Config] - 本地授权文件保存地点: %s", config.TierOneAuth.LocalAuthFilePath) + + // 初始化日志系统 + InitLogger() - // 这里可以添加从文件加载配置的逻辑,比如使用标准库读取JSON或YAML文件 - // 但目前使用默认值替代 }) - return nil + + return err } // GetConfig 获取配置 func GetConfig() *Config { return &config } + +// InitLogger 初始化日志系统 +// 从环境变量CMII_UAV_WATCHDOG_DEBUG读取配置,决定是否开启debug日志 +func InitLogger() { + // 获取Logger单例实例 + logger := wdd_log.GetInstance() + + // 设置是否启用Debug日志 + logger.SetEnableDebug(config.Server.Debug) + + // 记录日志初始化信息 + logger.Info("日志系统初始化完成,Debug日志状态: %v", config.Server.Debug) +} diff --git a/cmii-uav-watchdog/config/config.yaml b/cmii-uav-watchdog/config/config.yaml index 11dea65..2aa0626 100644 --- a/cmii-uav-watchdog/config/config.yaml +++ b/cmii-uav-watchdog/config/config.yaml @@ -1,10 +1,16 @@ server: - port: "8080" + port: "8080" # 服务器端口 + debug: true -auth: - secret: "your-secret-key-for-totp" - auth_file: "./data/auth.json" - time_offset_allowed: 3600 # 允许的时间偏移,单位秒 +tier_one_auth: + tier_one_secret: "NK537TIWSUOFIS7SYCUJ6A7FPOGFVM3UH67TJRX3IYQAHKZXK2X7SBAA6JOXZVSV3U6K5YZUX7Q6TWOPK6YCRU6MIML33ZJFBN55I2Q" # TOTP密钥 + time_offset_allowed: 30 # 允许的时间偏移(秒) watchdog_center: - url: "http://center-service:8080/api" \ No newline at end of file + url: "https://watchdog-center.example.com" # 一级授权中心地址 + +project: + project_namespace: "uavcloud-devflight" # 项目命名空间 + +tier_two_auth: + tier_two_secret: "your_tier_two_secret_here" # 二级授权密钥 diff --git a/cmii-uav-watchdog/controllers/auth_controller.go b/cmii-uav-watchdog/controllers/auth_controller.go index 0ea03e5..7e1fc45 100644 --- a/cmii-uav-watchdog/controllers/auth_controller.go +++ b/cmii-uav-watchdog/controllers/auth_controller.go @@ -2,10 +2,10 @@ package controllers import ( models2 "cmii-uav-watchdog-common/models" - "cmii-uav-watchdog/models" + "cmii-uav-watchdog-common/wdd_log" "cmii-uav-watchdog/services" "net/http" - + "github.com/gin-gonic/gin" ) @@ -26,59 +26,52 @@ func (ac *AuthController) GenerateAuthFile(c *gin.Context) { // 生成授权文件 authFile, err := ac.authService.GenerateAuthorizationFile() if err != nil { - c.JSON(http.StatusInternalServerError, models.Response{ - Code: 500, - Message: "生成授权文件失败", - Data: nil, - }) + c.JSON(http.StatusInternalServerError, authFile) return } - - c.JSON(http.StatusOK, models.Response{ - Code: 200, - Message: "生成授权文件成功", - Data: authFile, - }) + + c.JSON(http.StatusOK, authFile) } // ReceiveAuthCode 接收授权码 func (ac *AuthController) ReceiveAuthCode(c *gin.Context) { var authCode models2.AuthorizationCode if err := c.ShouldBindJSON(&authCode); err != nil { - c.JSON(http.StatusBadRequest, models.Response{ - Code: 400, - Message: "无效的请求参数", - Data: nil, - }) + c.JSON(http.StatusBadRequest, authCode) return } - + // 处理授权码 err := ac.authService.ProcessAuthorizationCode(authCode) if err != nil { - c.JSON(http.StatusInternalServerError, models.Response{ - Code: 500, - Message: "处理授权码失败: " + err.Error(), - Data: nil, - }) + wdd_log.Error("处理授权码失败: %v", err) + c.JSON(http.StatusInternalServerError, authCode) return } - - c.JSON(http.StatusOK, models.Response{ - Code: 200, - Message: "处理授权码成功", - Data: nil, - }) + + c.JSON(http.StatusOK, authCode) } // NotifyAuthInfo 通知授权信息 func (ac *AuthController) NotifyAuthInfo(c *gin.Context) { // 获取授权信息 - authInfo := ac.authService.GetAuthorizationInfo() - - c.JSON(http.StatusOK, models.Response{ - Code: 200, - Message: "获取授权信息成功", - Data: authInfo, + authInfo, err := ac.authService.GetAuthorizationInfo() + if err != nil { + c.JSON(http.StatusInternalServerError, err) + return + } + + c.JSON(http.StatusOK, authInfo) +} + +// GetAllAuthorizedHosts 获取所有已授权的主机信息 +func (ac *AuthController) GetAllAuthorizedHosts(c *gin.Context) { + // 获取所有已授权的主机信息 + authorizedHosts := ac.authService.GetAllAuthorizedHosts() + + // 返回已授权主机信息 + c.JSON(http.StatusOK, gin.H{ + "authorized_host_count": len(authorizedHosts), + "authorized_hosts": authorizedHosts, }) } diff --git a/cmii-uav-watchdog/controllers/cmii_controller.go b/cmii-uav-watchdog/controllers/cmii_controller.go new file mode 100644 index 0000000..8910e4d --- /dev/null +++ b/cmii-uav-watchdog/controllers/cmii_controller.go @@ -0,0 +1,24 @@ +package controllers + +import ( + "cmii-uav-watchdog/services" + + "github.com/gin-gonic/gin" +) + +// CMIIController CMII控制器 +type CMIIController struct { + cmiiService *services.CMIIService +} + +// NewCMIIController 创建CMII控制器 +func NewCMIIController() *CMIIController { + return &CMIIController{ + cmiiService: services.NewCMIIService(), + } +} + +// HandleHostInfo 处理主机信息 +func (cc *CMIIController) HandleHostInfo(c *gin.Context) { + cc.cmiiService.HandleHostInfo(c) +} diff --git a/cmii-uav-watchdog/controllers/heartbeat_controller.go b/cmii-uav-watchdog/controllers/heartbeat_controller.go index 17161e0..38db0b0 100644 --- a/cmii-uav-watchdog/controllers/heartbeat_controller.go +++ b/cmii-uav-watchdog/controllers/heartbeat_controller.go @@ -2,10 +2,10 @@ package controllers import ( models2 "cmii-uav-watchdog-common/models" - "cmii-uav-watchdog/models" + "cmii-uav-watchdog-common/wdd_log" "cmii-uav-watchdog/services" "net/http" - + "github.com/gin-gonic/gin" ) @@ -25,28 +25,36 @@ func NewHeartbeatController() *HeartbeatController { func (hc *HeartbeatController) HandleHeartbeat(c *gin.Context) { var req models2.HeartbeatRequest if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, models.Response{ - Code: 400, - Message: "无效的请求参数", - Data: nil, - }) + c.JSON(http.StatusBadRequest, req) return } - + // 处理心跳 resp, err := hc.heartbeatService.ProcessHeartbeat(req) if err != nil { - c.JSON(http.StatusInternalServerError, models.Response{ - Code: 500, - Message: "处理心跳失败: " + err.Error(), - Data: nil, + c.JSON(http.StatusInternalServerError, resp) + return + } + + c.JSON(http.StatusOK, resp) +} + +// GetAllHeartbeatHosts 获取所有心跳主机信息 +func (hc *HeartbeatController) GetAllHeartbeatHosts(c *gin.Context) { + // 获取所有主机信息 + hostInfoMap, err := hc.heartbeatService.GetAllHostInfo() + if err != nil { + wdd_log.Error("获取所有心跳主机信息失败: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "获取所有心跳主机信息失败", + "message": err.Error(), }) return } - - c.JSON(http.StatusOK, models.Response{ - Code: 200, - Message: "处理心跳成功", - Data: resp, + + // 返回主机信息 + c.JSON(http.StatusOK, gin.H{ + "host_count": len(hostInfoMap), + "hosts": hostInfoMap, }) } diff --git a/cmii-uav-watchdog/go.mod b/cmii-uav-watchdog/go.mod index ee4ffae..a13c124 100644 --- a/cmii-uav-watchdog/go.mod +++ b/cmii-uav-watchdog/go.mod @@ -2,15 +2,15 @@ module cmii-uav-watchdog go 1.24 -toolchain go1.24.1 +toolchain go1.24.0 require ( cmii-uav-watchdog-common v0.0.0 - cmii-uav-watchdog-otp v0.0.0 github.com/gin-gonic/gin v1.9.1 ) require ( + cmii-uav-watchdog-otp v0.0.0 // indirect github.com/bytedance/sonic v1.9.1 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect diff --git a/cmii-uav-watchdog/go.sum b/cmii-uav-watchdog/go.sum index ccc05b5..697daa3 100644 --- a/cmii-uav-watchdog/go.sum +++ b/cmii-uav-watchdog/go.sum @@ -1,80 +1,18 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -85,95 +23,19 @@ github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -181,35 +43,12 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ= -github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= -github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= -github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -217,341 +56,31 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= -github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= -go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/cmii-uav-watchdog/main.go b/cmii-uav-watchdog/main.go index 76c1f08..8803205 100644 --- a/cmii-uav-watchdog/main.go +++ b/cmii-uav-watchdog/main.go @@ -1,26 +1,25 @@ package main import ( + "cmii-uav-watchdog-common/wdd_log" "cmii-uav-watchdog/config" "cmii-uav-watchdog/routes" "cmii-uav-watchdog/services" - "log" "time" ) func main() { // 初始化配置信息 - err := config.LoadConfig("./config/config.yaml") + err := config.LoadConfig() if err != nil { - log.Fatalf("加载配置文件失败: %v", err) - return + panic(err) } // 初始化授权服务(使用单例模式) authService := services.NewAuthService() if authService == nil { - log.Fatalf("初始化授权服务失败") + wdd_log.Fatal("初始化授权服务失败") return } @@ -40,11 +39,11 @@ func main() { // 启动服务 port := config.GetConfig().Server.Port if port == "" { - port = "8080" + port = "8990" } - log.Printf("服务启动在 :%s", port) + wdd_log.Info("服务启动在 :%s", port) if err := r.Run(":" + port); err != nil { - log.Fatalf("服务启动失败: %v", err) + wdd_log.Error("服务启动失败: %v", err) } } diff --git a/cmii-uav-watchdog/models/response.go b/cmii-uav-watchdog/models/response.go deleted file mode 100644 index b97616a..0000000 --- a/cmii-uav-watchdog/models/response.go +++ /dev/null @@ -1,8 +0,0 @@ -package models - -// Response 通用响应模型 -type Response struct { - Code int `json:"code"` // 状态码 - Message string `json:"message"` // 响应消息 - Data interface{} `json:"data"` // 响应数据 -} diff --git a/cmii-uav-watchdog/routes/router.go b/cmii-uav-watchdog/routes/router.go index e3d5fda..10310dc 100644 --- a/cmii-uav-watchdog/routes/router.go +++ b/cmii-uav-watchdog/routes/router.go @@ -21,16 +21,25 @@ func SetupRouter() *gin.Engine { auth := api.Group("/authorization") { authController := controllers.NewAuthController() - auth.POST("/generate", authController.GenerateAuthFile) // 授权文件生成 - auth.POST("/code", authController.ReceiveAuthCode) // 授权码接收 - auth.POST("/notify", authController.NotifyAuthInfo) // 授权信息通知 + auth.GET("/generate", authController.GenerateAuthFile) // 授权文件生成 + auth.POST("/auth", authController.ReceiveAuthCode) // 授权码接收 + auth.POST("/info", authController.NotifyAuthInfo) // 授权信息通知 + auth.GET("/hosts", authController.GetAllAuthorizedHosts) // 获取所有已授权主机信息 } // 心跳检测路由 heartbeat := api.Group("/heartbeat") { heartbeatController := controllers.NewHeartbeatController() - heartbeat.POST("", heartbeatController.HandleHeartbeat) // 心跳检测 + heartbeat.POST("", heartbeatController.HandleHeartbeat) // 心跳检测 + heartbeat.GET("/hosts", heartbeatController.GetAllHeartbeatHosts) // 获取所有心跳主机信息 + } + + // 暴露CMII的接口 + cmii := api.Group("/cmii") + { + cmiiController := controllers.NewCMIIController() + cmii.POST("/host/info/all", cmiiController.HandleHostInfo) // 处理主机信息 } } diff --git a/cmii-uav-watchdog/services/auth_service.go b/cmii-uav-watchdog/services/auth_service.go index 7dd9987..04d91f9 100644 --- a/cmii-uav-watchdog/services/auth_service.go +++ b/cmii-uav-watchdog/services/auth_service.go @@ -2,14 +2,15 @@ package services import ( models2 "cmii-uav-watchdog-common/models" + "cmii-uav-watchdog-common/totp_tier_one" + "cmii-uav-watchdog-common/totp_tier_two" + "cmii-uav-watchdog-common/utils" + "cmii-uav-watchdog-common/wdd_log" "cmii-uav-watchdog/config" - "cmii-uav-watchdog/utils" "encoding/json" "errors" - "log" "os" "sync" - "time" ) // 单例相关变量 @@ -21,11 +22,11 @@ var ( // AuthService 授权服务 type AuthService struct { - mu sync.RWMutex - hostInfoSet map[string]models2.HostInfo // 主机信息集合 - authorizationInfo models2.AuthorizationStorage // 授权信息 - totpService *TOTPService - initialized bool + mu sync.RWMutex + heartBeatHostInfoMap map[string]models2.HostInfo // 主机信息集合 + authorizationInfo *models2.AuthorizationStorage // 授权信息 + totpService *TOTPService + initialized bool } // NewAuthService 创建授权服务(单例模式) @@ -42,20 +43,25 @@ func NewAuthService() *AuthService { // 创建新实例 service := &AuthService{ - hostInfoSet: make(map[string]models2.HostInfo), - totpService: NewTOTPService(), - initialized: false, + heartBeatHostInfoMap: make(map[string]models2.HostInfo), + totpService: NewTOTPService(), + initialized: false, } // 尝试从本地加载授权信息 service.loadAuthorizationInfo() + // 确保 authorizationInfo 不为 nil + if service.authorizationInfo == nil { + service.authorizationInfo = &models2.AuthorizationStorage{} + } + // 判断 项目级别的 TOTP密钥是否为空 // 若为空 则生成一个 二级TOTP密钥 然后持久化写入到授权文件中 if service.authorizationInfo.SecondTOTPSecret == "" { - secondTOTPSecret, err := service.totpService.GenerateTierTwoTOTPSecret() + secondTOTPSecret, err := totp_tier_two.GenerateTierTwoTOTPSecret() if err != nil { - log.Printf("生成二级TOTP密钥失败: %v", err) + wdd_log.Error("生成二级TOTP密钥失败: %v", err) return } service.authorizationInfo.SecondTOTPSecret = secondTOTPSecret @@ -63,7 +69,7 @@ func NewAuthService() *AuthService { // 持久化写入到授权文件中 err = service.saveAuthorizationInfo() if err != nil { - log.Printf("持久化写入授权文件失败: %v", err) + wdd_log.Error("持久化写入授权文件失败: %v", err) return } } @@ -80,8 +86,13 @@ func (as *AuthService) AddHostInfo(hostInfo models2.HostInfo) { as.mu.Lock() defer as.mu.Unlock() - hostKey := utils.GenerateHostKey(hostInfo) - as.hostInfoSet[hostKey] = hostInfo + hostKey, err := totp_tier_one.EncryptHostInfo(hostInfo, as.totpService.tierOneSecret) + if err != nil { + wdd_log.Error("加密主机信息失败: %v", err) + return + } + + as.heartBeatHostInfoMap[hostKey] = hostInfo } // GenerateAuthorizationFile 生成授权文件 @@ -90,96 +101,143 @@ func (as *AuthService) GenerateAuthorizationFile() (*models2.AuthorizationFile, defer as.mu.RUnlock() // 检查是否有主机信息 - if len(as.hostInfoSet) == 0 { + if len(as.heartBeatHostInfoMap) == 0 { return nil, errors.New("没有可用的主机信息") } - // 生成TOTP验证码 - totpCode, err := as.totpService.GenerateTierTwoTOTPCode(as.authorizationInfo.SecondTOTPSecret) + // 加密主机信息 + encryptedHostMap := make(map[string]models2.HostInfo) + for _, hostInfo := range as.heartBeatHostInfoMap { + encrypted, err := totp_tier_one.EncryptHostInfo(hostInfo, as.totpService.tierOneSecret) + if err != nil { + wdd_log.Error("加密主机信息失败: %v hostInfo NetworkInfo: %v", err, hostInfo.NetInfo) + } + encryptedHostMap[encrypted] = hostInfo + } + + // 获取项目命名空间 + projectNamespace := config.GetConfig().Project.ProjectNamespace + encryptedNamespace, err := totp_tier_one.Encrypt(projectNamespace, projectNamespace) if err != nil { return nil, err } - // 获取当前时间 - now := time.Now() - - // 准备初次授权时间 - firstAuthTime := now - if as.initialized { - firstAuthTime = as.authorizationInfo.FirstAuthTime - } - - // 计算时间偏移 - timeOffset := now.Unix() - firstAuthTime.Unix() - - // 加密主机信息 - encryptedHosts := make([]string, 0) - for _, hostInfo := range as.hostInfoSet { - encrypted, err := utils.EncryptHostInfo(hostInfo) - if err != nil { - return nil, err - } - encryptedHosts = append(encryptedHosts, encrypted) + // 生成TOTP验证码 + tierOneSecret := config.GetConfig().TierOneAuth.TierOneSecret + tierOneTOTPCode, err := totp_tier_one.GenerateTierOneTOTPCode(tierOneSecret) + if err != nil { + wdd_log.Error("生成TOTP验证码失败: %v", err) + return nil, err } // 创建授权文件 authFile := &models2.AuthorizationFile{ - EncryptedHosts: encryptedHosts, - TOTPCode: totpCode, - CurrentTime: now, - FirstAuthTime: firstAuthTime, - TimeOffset: timeOffset, + EncryptedHostMap: encryptedHostMap, + TOTPCode: tierOneTOTPCode, + CurrentTime: utils.CurentTimeString(), + ProjectNamespace: projectNamespace, + EncryptedNamespace: encryptedNamespace, } return authFile, nil } // ProcessAuthorizationCode 处理授权码 -func (as *AuthService) ProcessAuthorizationCode(code models2.AuthorizationCode) error { +func (as *AuthService) ProcessAuthorizationCode(authCode models2.AuthorizationCode) error { as.mu.Lock() defer as.mu.Unlock() // 验证TOTP - if !as.totpService.VerifyTierTwoTOTPCode(code.TOTPCode, as.authorizationInfo.SecondTOTPSecret) { + if !as.totpService.VerifyTierOneTOTP(authCode.TOTPCode) { + wdd_log.Error("无效的授权码: TOTP验证失败") return errors.New("无效的授权码: TOTP验证失败") } + // 解密项目命名空间 + projectNamespace, err := totp_tier_one.Decrypt(authCode.EncryptedNamespace, authCode.ProjectNamespace) + if err != nil { + wdd_log.Error("解密项目命名空间失败: %v", err) + return err + } + + // 验证项目命名空间 + if projectNamespace != config.GetConfig().Project.ProjectNamespace { + wdd_log.Error("无效的授权码: 项目命名空间不匹配") + return errors.New("无效的授权码: 项目命名空间不匹配") + } + + // 解密服务器信息 + encryptedServerInfoMap := authCode.EncryptedHostMap + serverInfoMap := make(map[string]models2.HostInfo) + for encrypted, hostInfo := range encryptedServerInfoMap { + isOK, err := totp_tier_one.DecryptHostInfo(encrypted, hostInfo, as.totpService.tierOneSecret) + if !isOK || err != nil { + wdd_log.Error("解密服务器信息失败: %v hostInfo NetworkInfo: %v", err, hostInfo.NetInfo) + continue + } + // 将解密后的主机信息添加到serverInfoMap中 + serverInfoMap[encrypted] = hostInfo + // 将解密后的主机信息添加到hostInfoSet中 + as.heartBeatHostInfoMap[encrypted] = hostInfo + } + // 获取当前时间 - now := time.Now() + now := utils.CurentTimeString() // 准备初次授权时间 firstAuthTime := now if as.initialized { - firstAuthTime = as.authorizationInfo.FirstAuthTime + if as.authorizationInfo.FirstAuthTime != "" { + wdd_log.Debug("已存在初次授权时间: %s", as.authorizationInfo.FirstAuthTime) + firstAuthTime = as.authorizationInfo.FirstAuthTime + } } // 计算授权时间偏移 - timeOffset := now.Unix() - code.CurrentTime.Unix() + nowTime, err := utils.ParseTimeString(now) + if err != nil { + wdd_log.Error("解析当前时间失败: %v", err) + return err + } + firstAuthTimeTime, err := utils.ParseTimeString(firstAuthTime) + if err != nil { + wdd_log.Error("解析初次授权时间失败: %v", err) + return err + } + timeOffset := nowTime.Unix() - firstAuthTimeTime.Unix() // 加密授权码 - authCodeJSON, err := json.Marshal(code) + authCodeJSON, err := json.Marshal(authCode) if err != nil { + wdd_log.Error("加密授权码失败: %v", err) return err } - encryptedCode, err := utils.Encrypt(string(authCodeJSON), config.GetConfig().Auth.Secret) + // 加密授权码 全部加密 + encryptedCode, err := totp_tier_one.Encrypt(string(authCodeJSON), as.totpService.tierOneSecret) if err != nil { + wdd_log.Error("加密授权码失败: %v", err) return err } // 保存授权信息 - as.authorizationInfo = models2.AuthorizationStorage{ - EncryptedCode: encryptedCode, - FirstAuthTime: firstAuthTime, - TimeOffset: timeOffset, - AuthorizedHosts: code.EncryptedHosts, + as.authorizationInfo = &models2.AuthorizationStorage{ + EncryptedAuthrizationCode: encryptedCode, + FirstAuthTime: firstAuthTime, + TimeOffset: timeOffset, + AuthorizedHostMap: serverInfoMap, + SecondTOTPSecret: as.authorizationInfo.SecondTOTPSecret, + ProjectNamespace: projectNamespace, + EncryptedNamespace: authCode.EncryptedNamespace, } // 保存到文件 if err := as.saveAuthorizationInfo(); err != nil { + wdd_log.Error("保存授权信息失败: %v", err) return err } + // 设置初始化状态 as.initialized = true return nil @@ -195,19 +253,14 @@ func (as *AuthService) IsHostAuthorized(hostInfo models2.HostInfo) bool { } // 加密主机信息 - encrypted, err := utils.EncryptHostInfo(hostInfo) + encrypted, err := totp_tier_one.EncryptHostInfo(hostInfo, as.totpService.tierOneSecret) if err != nil { return false } // 检查是否在已授权列表中 - for _, host := range as.authorizationInfo.AuthorizedHosts { - if host == encrypted { - return true - } - } - - return false + _, ok := as.authorizationInfo.AuthorizedHostMap[encrypted] + return ok } // VerifyAuthorizationTime 验证授权时间有效性 @@ -220,43 +273,87 @@ func (as *AuthService) VerifyAuthorizationTime() { } // 获取当前时间和存储的初次授权时间 - now := time.Now() + now := utils.CurentTimeString() storedOffset := as.authorizationInfo.TimeOffset - + nowTime, err := utils.ParseTimeString(now) + if err != nil { + return + } + firstAuthTime, err := utils.ParseTimeString(as.authorizationInfo.FirstAuthTime) + if err != nil { + return + } // 计算实际时间偏移 - actualOffset := now.Unix() - as.authorizationInfo.FirstAuthTime.Unix() + actualOffset := nowTime.Unix() - firstAuthTime.Unix() // 计算偏差 offsetDiff := actualOffset - storedOffset // 获取允许的时间偏移 - allowedOffset := config.GetConfig().Auth.TimeOffsetAllowed + allowedOffset := config.GetConfig().TierOneAuth.TimeOffsetAllowed // 检查偏差是否超过允许范围 if offsetDiff > allowedOffset || offsetDiff < -allowedOffset { - log.Printf("检测到时间篡改! 存储偏移: %d, 实际偏移: %d, 差值: %d", + wdd_log.Warn("检测到时间篡改! 存储偏移: %d, 实际偏移: %d, 差值: %d", storedOffset, actualOffset, offsetDiff) // 这里可以添加更多的处理逻辑,例如发送警告、禁用授权等 } } // GetAuthorizationInfo 获取授权信息 -func (as *AuthService) GetAuthorizationInfo() interface{} { +func (as *AuthService) GetAuthorizationInfo() (models2.AuthorizationStorage, error) { as.mu.RLock() defer as.mu.RUnlock() if !as.initialized { - return map[string]interface{}{ - "authorized": false, - "message": "未授权", - } + return models2.AuthorizationStorage{}, errors.New("未授权") } - return map[string]interface{}{ - "authorized": true, - "authorized_hosts": len(as.authorizationInfo.AuthorizedHosts), - "first_auth_time": as.authorizationInfo.FirstAuthTime, + // 解密授权码 + decryptedCode, err := totp_tier_one.Decrypt(as.authorizationInfo.EncryptedAuthrizationCode, as.totpService.tierOneSecret) + if err != nil { + return models2.AuthorizationStorage{}, err } + + // 解析授权码 + var authorizationInfo models2.AuthorizationStorage + if err := json.Unmarshal([]byte(decryptedCode), &authorizationInfo); err != nil { + return models2.AuthorizationStorage{}, err + } + + return authorizationInfo, nil +} + +// GetAllHostInfo 获取所有主机信息(心跳检测到的所有主机) +func (as *AuthService) GetAllHostInfo() map[string]models2.HostInfo { + as.mu.RLock() + defer as.mu.RUnlock() + + // 返回主机信息集合的副本 + result := make(map[string]models2.HostInfo, len(as.heartBeatHostInfoMap)) + for k, v := range as.heartBeatHostInfoMap { + result[k] = v + } + + return result +} + +// GetAllAuthorizedHosts 获取所有已授权的主机信息 +func (as *AuthService) GetAllAuthorizedHosts() map[string]models2.HostInfo { + as.mu.RLock() + defer as.mu.RUnlock() + + if !as.initialized || as.authorizationInfo == nil { + return make(map[string]models2.HostInfo) + } + + // 返回已授权主机信息的副本 + result := make(map[string]models2.HostInfo, len(as.authorizationInfo.AuthorizedHostMap)) + for k, v := range as.authorizationInfo.AuthorizedHostMap { + result[k] = v + } + + return result } // saveAuthorizationInfo 保存授权信息到文件 @@ -266,30 +363,40 @@ func (as *AuthService) saveAuthorizationInfo() error { return err } - return os.WriteFile(config.GetConfig().Auth.AuthFilePath, data, 0600) + return os.WriteFile(config.GetConfig().TierOneAuth.LocalAuthFilePath, data, 0600) } // loadAuthorizationInfo 从文件加载授权信息 func (as *AuthService) loadAuthorizationInfo() { - filePath := config.GetConfig().Auth.AuthFilePath + filePath := config.GetConfig().TierOneAuth.LocalAuthFilePath // 检查文件是否存在 if _, err := os.Stat(filePath); os.IsNotExist(err) { + // 初始化一个空的授权信息 + as.authorizationInfo = &models2.AuthorizationStorage{} return } data, err := os.ReadFile(filePath) if err != nil { - log.Printf("读取授权文件失败: %v", err) + wdd_log.Error("读取授权文件失败: %v", err) + // 初始化一个空的授权信息 + as.authorizationInfo = &models2.AuthorizationStorage{} return } - var authInfo models2.AuthorizationStorage + var authInfo *models2.AuthorizationStorage if err := json.Unmarshal(data, &authInfo); err != nil { - log.Printf("解析授权文件失败: %v", err) + wdd_log.Error("解析授权文件失败: %v", err) + // 初始化一个空的授权信息 + as.authorizationInfo = &models2.AuthorizationStorage{} return } + if authInfo == nil { + authInfo = &models2.AuthorizationStorage{} + } + as.authorizationInfo = authInfo as.initialized = true } diff --git a/cmii-uav-watchdog/services/cmii_service.go b/cmii-uav-watchdog/services/cmii_service.go new file mode 100644 index 0000000..325193c --- /dev/null +++ b/cmii-uav-watchdog/services/cmii_service.go @@ -0,0 +1,36 @@ +package services + +import ( + "cmii-uav-watchdog-common/models" + "net/http" + + "github.com/gin-gonic/gin" +) + +// CMIIService CMII服务 +type CMIIService struct { + authService *AuthService +} + +// NewCMIIService 创建CMII服务 +func NewCMIIService() *CMIIService { + return &CMIIService{ + authService: NewAuthService(), + } +} + +// HandleHostInfo 处理主机信息 +func (cs *CMIIService) HandleHostInfo(c *gin.Context) { + + // 获取主机信息 + hostInfo := cs.authService.GetAllHostInfo() + + // 返回单纯的主机信息 + hostInfoList := make([]models.HostInfo, 0) + for _, host := range hostInfo { + hostInfoList = append(hostInfoList, host) + } + + // 返回主机信息 + c.JSON(http.StatusOK, hostInfoList) +} diff --git a/cmii-uav-watchdog/services/heartbeat_service.go b/cmii-uav-watchdog/services/heartbeat_service.go index ff50ebc..03767e6 100644 --- a/cmii-uav-watchdog/services/heartbeat_service.go +++ b/cmii-uav-watchdog/services/heartbeat_service.go @@ -2,8 +2,10 @@ package services import ( "cmii-uav-watchdog-common/models" + "cmii-uav-watchdog-common/totp_tier_two" + "cmii-uav-watchdog-common/utils" + "cmii-uav-watchdog-common/wdd_log" "errors" - "log" "time" ) @@ -30,6 +32,7 @@ func (hs *HeartbeatService) ProcessHeartbeat(req models.HeartbeatRequest) (*mode secondTOTPSecret := hs.authService.authorizationInfo.SecondTOTPSecret if secondTOTPSecret == "" { + wdd_log.Error("二级TOTP密钥为空") return nil, errors.New("二级TOTP密钥为空") } @@ -47,12 +50,13 @@ func (hs *HeartbeatService) ProcessHeartbeat(req models.HeartbeatRequest) (*mode } // 检查totp验证码是否有效 - if !hs.totpService.VerifyTierTwoTOTPCode(req.TOTPCode, secondTOTPSecret) { + if !totp_tier_two.VerifyTierTwoTOTPCode(req.TOTPCode, secondTOTPSecret) { // 解析认证主机的相关信息 // 计算 请求时间与当前时间的时间差 + utils.CurentTimeUnix() diff := time.Now().Unix() - req.Timestamp - log.Printf("心跳请求时间与当前时间的时间差: %d", diff) + wdd_log.Error("TOTP验证失败!心跳请求时间与当前时间的时间差: %d", diff) return nil, errors.New("无效的TOTP验证码,请检查系统时间是否正确!") } @@ -61,7 +65,7 @@ func (hs *HeartbeatService) ProcessHeartbeat(req models.HeartbeatRequest) (*mode authorized := hs.authService.IsHostAuthorized(req.HostInfo) // 生成TOTP验证码 - totpCode, err := hs.totpService.GenerateTierTwoTOTPCode(secondTOTPSecret) + totpCode, err := totp_tier_two.GenerateTierTwoTOTPCode(secondTOTPSecret) if err != nil { return nil, err } @@ -82,3 +86,16 @@ func (hs *HeartbeatService) isTimestampValid(timestamp int64) bool { // 允许5分钟的时间偏差 return diff < 300 && diff > -300 } + +// GetAllHostInfo 获取所有主机信息 +func (hs *HeartbeatService) GetAllHostInfo() (map[string]models.HostInfo, error) { + // 调用AuthService的方法获取所有主机信息 + hostInfoMap := hs.authService.GetAllHostInfo() + + // 如果没有主机信息,返回一个空的map + if len(hostInfoMap) == 0 { + return make(map[string]models.HostInfo), nil + } + + return hostInfoMap, nil +} diff --git a/cmii-uav-watchdog/services/totp_service.go b/cmii-uav-watchdog/services/totp_service.go index 096168c..61916dc 100644 --- a/cmii-uav-watchdog/services/totp_service.go +++ b/cmii-uav-watchdog/services/totp_service.go @@ -1,41 +1,31 @@ package services import ( + "cmii-uav-watchdog-common/totp_tier_one" "cmii-uav-watchdog/config" - "log" - "time" - - otp "cmii-uav-watchdog-otp" - "cmii-uav-watchdog-otp/totp" ) -var tierTwoTOTPSecretOpts = totp.GenerateOpts{ - SecretSize: 32, - Issuer: "cmii-uav-watchdog", - AccountName: "cmii-uav-watchdog", - Period: 30, - Secret: []byte{}, - Digits: otp.DigitsSix, - Algorithm: otp.AlgorithmSHA1, - Rand: nil, -} - // TOTPService TOTP服务 type TOTPService struct { - secret string + tierOneSecret string } // NewTOTPService 创建TOTP服务 func NewTOTPService() *TOTPService { + secret := config.GetConfig().TierOneAuth.TierOneSecret + if secret == "" { + panic("TierOne TOTP tierOneSecret is not set ! can not start the service!") + } + return &TOTPService{ - secret: config.GetConfig().Auth.Secret, + tierOneSecret: secret, } } // GenerateTierOneTOTP 生成一级TOTP验证码 func (ts *TOTPService) GenerateTierOneTOTP() (string, error) { // 使用当前时间生成TOTP - code, err := totp.GenerateCode(ts.secret, time.Now()) + code, err := totp_tier_one.GenerateTierOneTOTPCode(ts.tierOneSecret) if err != nil { return "", err } @@ -46,41 +36,7 @@ func (ts *TOTPService) GenerateTierOneTOTP() (string, error) { // VerifyTierOneTOTP 验证一级TOTP验证码 func (ts *TOTPService) VerifyTierOneTOTP(code string) bool { // 验证TOTP - valid := totp.Validate(code, ts.secret) - if !valid { - return false - } + valid := totp_tier_one.VerifyTierOneTOTPCode(code, ts.tierOneSecret) - return true -} - -// GenerateTierTwoTOTPSecret 生成二级TOTP密钥 -func (ts *TOTPService) GenerateTierTwoTOTPSecret() (string, error) { - secret, err := totp.Generate(tierTwoTOTPSecretOpts) - if err != nil { - log.Printf("生成TOTP密钥失败: %v", err) - return "", err - } - - return secret.Secret(), nil -} - -// GenerateTierTwoTOTPCode 生成二级TOTP验证码 -func (ts *TOTPService) GenerateTierTwoTOTPCode(secret string) (string, error) { - code, err := totp.GenerateCode(secret, time.Now()) - if err != nil { - return "", err - } - return code, nil -} - -// VerifyTierTwoTOTPCode 验证二级TOTP验证码 -func (ts *TOTPService) VerifyTierTwoTOTPCode(code string, secret string) bool { - validateOpts := totp.ValidateOpts{} - validateOpts.ConvertToValidateOpts(tierTwoTOTPSecretOpts) - valid, err := totp.ValidateCustom(code, secret, time.Now(), validateOpts) - if err != nil { - return false - } return valid } diff --git a/cmii-uav-watchdog/utils/crypto.go b/cmii-uav-watchdog/utils/crypto.go deleted file mode 100644 index f5a408a..0000000 --- a/cmii-uav-watchdog/utils/crypto.go +++ /dev/null @@ -1,113 +0,0 @@ -package utils - -import ( - "cmii-uav-watchdog-common/models" - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "crypto/sha256" - "encoding/base64" - "encoding/json" - "errors" - "io" -) - -// Encrypt 加密字符串 -func Encrypt(plaintext string, key string) (string, error) { - // 创建hash - hasher := sha256.New() - hasher.Write([]byte(key)) - keyBytes := hasher.Sum(nil) - - // 创建cipher - block, err := aes.NewCipher(keyBytes) - if err != nil { - return "", err - } - - // 创建gcm - gcm, err := cipher.NewGCM(block) - if err != nil { - return "", err - } - - // 创建nonce - nonce := make([]byte, gcm.NonceSize()) - if _, err := io.ReadFull(rand.Reader, nonce); err != nil { - return "", err - } - - // 加密 - ciphertext := gcm.Seal(nonce, nonce, []byte(plaintext), nil) - - // 编码为base64 - return base64.StdEncoding.EncodeToString(ciphertext), nil -} - -// Decrypt 解密字符串 -func Decrypt(encrypted string, key string) (string, error) { - // 解码base64 - ciphertext, err := base64.StdEncoding.DecodeString(encrypted) - if err != nil { - return "", err - } - - // 创建hash - hasher := sha256.New() - hasher.Write([]byte(key)) - keyBytes := hasher.Sum(nil) - - // 创建cipher - block, err := aes.NewCipher(keyBytes) - if err != nil { - return "", err - } - - // 创建gcm - gcm, err := cipher.NewGCM(block) - if err != nil { - return "", err - } - - // 检查长度 - if len(ciphertext) < gcm.NonceSize() { - return "", errors.New("密文太短") - } - - // 分离nonce和密文 - nonce, ciphertext := ciphertext[:gcm.NonceSize()], ciphertext[gcm.NonceSize():] - - // 解密 - plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) - if err != nil { - return "", err - } - - return string(plaintext), nil -} - -// EncryptHostInfo 加密主机信息 -func EncryptHostInfo(hostInfo models.HostInfo) (string, error) { - // 序列化主机信息 - data, err := json.Marshal(hostInfo) - if err != nil { - return "", err - } - - // 计算SHA256作为统一标识 - hasher := sha256.New() - hasher.Write(data) - hash := base64.StdEncoding.EncodeToString(hasher.Sum(nil)) - - return hash, nil -} - -// GenerateHostKey 生成主机唯一标识 -func GenerateHostKey(hostInfo models.HostInfo) string { - // 序列化主机信息 - key := hostInfo.UUID - if key == "" { - key = hostInfo.MAC - } - return key -} diff --git a/main.go b/main.go deleted file mode 100644 index 50f9fc7..0000000 --- a/main.go +++ /dev/null @@ -1 +0,0 @@ -package cmii_uav_watchdog