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