版本封存
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -18,3 +18,5 @@ build
|
|||||||
|
|
||||||
.cursor/rules
|
.cursor/rules
|
||||||
|
|
||||||
|
cmii-uav-watchdog-project-设计结构
|
||||||
|
|
||||||
|
|||||||
13
.run/agent-35-70.run.xml
Normal file
13
.run/agent-35-70.run.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="agent-35-70" type="GoApplicationRunConfiguration" factoryName="Go Application">
|
||||||
|
<module name="cmii-uav-watchdog-project" />
|
||||||
|
<target name="wdd-dev-35.70" />
|
||||||
|
<working_directory value="$PROJECT_DIR$/cmii-uav-watchdog-agent" />
|
||||||
|
<kind value="PACKAGE" />
|
||||||
|
<package value="cmii-uav-watchdog-agent/cmd" />
|
||||||
|
<directory value="$PROJECT_DIR$" />
|
||||||
|
<filePath value="$PROJECT_DIR$/cmii-uav-watchdog-agent/cmd/start_up.go" />
|
||||||
|
<option name="build_on_remote_target" value="true" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
47
cmii-uav-watchdog-agent/cmd/env_info.go
Normal file
47
cmii-uav-watchdog-agent/cmd/env_info.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
@@ -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("正在关闭服务...")
|
||||||
|
// }
|
||||||
|
|||||||
@@ -1,16 +1,20 @@
|
|||||||
package cmd
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cmii-uav-watchdog-common/wdd_log"
|
||||||
"flag"
|
"flag"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 是否是Debug模式
|
||||||
|
var DebugMode = false
|
||||||
|
|
||||||
var (
|
var (
|
||||||
businessProgramType = flag.String("business-program-type", "", "Type of business program (java or python)")
|
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")
|
businessProgramPath = flag.String("business-program-path", "", "Path to the business program file")
|
||||||
@@ -19,45 +23,221 @@ var (
|
|||||||
mu sync.Mutex
|
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 {
|
func startBusinessProcess(programType, programPath string) *exec.Cmd {
|
||||||
var cmd *exec.Cmd
|
var cmd *exec.Cmd
|
||||||
switch programType {
|
switch programType {
|
||||||
case "java":
|
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":
|
case "python":
|
||||||
cmd = exec.Command("python", programPath)
|
cmd = exec.Command("python", programPath)
|
||||||
default:
|
default:
|
||||||
log.Fatalf("Unsupported business program type: %s", programType)
|
wdd_log.Error("不支持的业务程序类型: %s", programType)
|
||||||
}
|
}
|
||||||
return cmd
|
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() {
|
func main() {
|
||||||
// 解析命令行参数
|
// 解析命令行参数
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if *businessProgramType == "" || *businessProgramPath == "" {
|
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)
|
signalChan := make(chan os.Signal, 1)
|
||||||
signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM)
|
signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
||||||
|
// StartHeartbeatDetection(signalChan)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for sig := range signalChan {
|
for sig := range signalChan {
|
||||||
log.Printf("Received signal: %v", sig)
|
wdd_log.Info("接收到信号: %v", sig)
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
stopRequested = true
|
stopRequested = true
|
||||||
if currentCmd != nil && currentCmd.Process != nil {
|
if currentCmd != nil && currentCmd.Process != nil {
|
||||||
// 发送 SIGTERM 给业务进程
|
// 发送 SIGTERM 给业务进程
|
||||||
if err := currentCmd.Process.Signal(syscall.SIGTERM); err != nil {
|
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 秒后强制杀死进程
|
// 等待 10 秒后强制杀死进程
|
||||||
time.AfterFunc(10*time.Second, func() {
|
time.AfterFunc(10*time.Second, func() {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
defer mu.Unlock()
|
defer mu.Unlock()
|
||||||
if currentCmd != nil && currentCmd.Process != nil {
|
if currentCmd != nil && currentCmd.Process != nil {
|
||||||
log.Println("Graceful shutdown timeout, sending SIGKILL")
|
wdd_log.Warn("优雅关闭超时,发送SIGKILL信号")
|
||||||
currentCmd.Process.Kill()
|
currentCmd.Process.Kill()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -66,12 +246,19 @@ func main() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// 授权检测
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
StartHeartbeatDetection(signalChan)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// 主循环
|
// 主循环
|
||||||
for {
|
for {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
if stopRequested {
|
if stopRequested {
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
log.Println("Shutting down due to stop request")
|
wdd_log.Info("收到停止请求,正在关闭")
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
@@ -82,11 +269,16 @@ func main() {
|
|||||||
|
|
||||||
// 启动业务进程
|
// 启动业务进程
|
||||||
if err := cmd.Start(); err != nil {
|
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)
|
time.Sleep(5 * time.Second)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 业务进程启动成功
|
||||||
|
if *businessProgramType == "java" {
|
||||||
|
wdd_log.Info("[CONTAINER] 程序启动成功!")
|
||||||
|
}
|
||||||
|
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
currentCmd = cmd
|
currentCmd = cmd
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
@@ -98,21 +290,21 @@ func main() {
|
|||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Business process exited with error: %v", err)
|
wdd_log.Error("业务进程异常退出: %v", err)
|
||||||
} else {
|
} else {
|
||||||
log.Println("Business process exited normally")
|
wdd_log.Info("业务进程正常退出")
|
||||||
}
|
}
|
||||||
|
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
if stopRequested {
|
if stopRequested {
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
log.Println("Shutting down due to stop request")
|
wdd_log.Info("收到停止请求,正在关闭")
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
|
|
||||||
// 等待 5 秒后重启
|
// 等待 5 秒后重启
|
||||||
log.Println("Restarting business process in 5 seconds...")
|
wdd_log.Info("5秒后重启业务进程...")
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,76 +1,95 @@
|
|||||||
package cmd
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmii-uav-watchdog-agent/host_info"
|
"cmii-uav-watchdog-agent/host_info"
|
||||||
"cmii-uav-watchdog-agent/rpc"
|
"cmii-uav-watchdog-agent/rpc"
|
||||||
"cmii-uav-watchdog-agent/totp"
|
|
||||||
"cmii-uav-watchdog-common/models"
|
"cmii-uav-watchdog-common/models"
|
||||||
"fmt"
|
"cmii-uav-watchdog-common/totp_tier_two"
|
||||||
"log"
|
"cmii-uav-watchdog-common/wdd_log"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
var (
|
||||||
// 最大重试次数
|
// 最大重试次数
|
||||||
maxRetryCount = 5
|
maxRetryCount = 12
|
||||||
|
|
||||||
// 默认心跳检测间隔
|
// 默认心跳检测间隔
|
||||||
defaultHeartbeatInterval = 30 * time.Second
|
defaultHeartbeatInterval = 2 * time.Hour
|
||||||
|
|
||||||
// 检测失败后的等待间隔
|
// 检测失败后的等待间隔
|
||||||
failWaitInterval = 5 * time.Second
|
failWaitInterval = 1 * time.Hour
|
||||||
|
|
||||||
// 环境变量名称
|
|
||||||
appNameEnv = "APP_NAME"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// 启动心跳检测
|
var tierTwoTotpSecret = ""
|
||||||
func StartHeartbeatDetection() {
|
|
||||||
log.Println("启动心跳检测任务...")
|
// 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客户端
|
// 创建RPC客户端
|
||||||
client := rpc.NewClient(nil)
|
heartbeatURL := os.Getenv("WATCHDOG_HEARTBEAT_URL")
|
||||||
|
client := rpc.NewClient(nil, heartbeatURL)
|
||||||
|
|
||||||
// 监听终止信号
|
wdd_log.Info("心跳检测URL: %s", client.GetHeartbeatURL())
|
||||||
signalChan := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM)
|
|
||||||
|
|
||||||
// 失败计数器
|
// 失败计数器
|
||||||
failCount := 0
|
failCount := 1
|
||||||
|
|
||||||
// 心跳检测循环
|
// 心跳检测循环
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-signalChan:
|
case <-signalChan:
|
||||||
log.Println("收到终止信号,停止心跳检测")
|
wdd_log.Info("收到终止信号,停止心跳检测")
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
// 尝试发送心跳请求
|
// 尝试发送心跳请求
|
||||||
authorized, err := sendHeartbeat(client)
|
authorized, err := sendHeartbeat(client)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("心跳检测失败: %v", err)
|
wdd_log.Error("第 %d 次心跳检测失败: %v", failCount, err)
|
||||||
failCount++
|
failCount++
|
||||||
} else if !authorized {
|
} else if !authorized {
|
||||||
log.Println("未获得授权")
|
wdd_log.Warn("第 %d 次心跳检测未获得授权", failCount)
|
||||||
failCount++
|
failCount++
|
||||||
} else {
|
} else {
|
||||||
// 检测成功,重置失败计数
|
// 检测成功,重置失败计数
|
||||||
failCount = 0
|
failCount = 1
|
||||||
log.Println("心跳检测成功,已获得授权")
|
wdd_log.Info("第 %d 次心跳检测成功,已获得授权", failCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否达到最大失败次数
|
// 检查是否达到最大失败次数
|
||||||
if failCount >= maxRetryCount {
|
if failCount >= maxRetryCount {
|
||||||
log.Printf("心跳检测连续失败 %d 次,发送终止信号", failCount)
|
wdd_log.Fatal("心跳检测连续失败 %d 次,发送终止信号!!", failCount)
|
||||||
// 发送终止信号给start_up.go
|
signalChan <- syscall.SIGTERM
|
||||||
process, err := os.FindProcess(os.Getpid())
|
|
||||||
if err == nil {
|
|
||||||
process.Signal(syscall.SIGTERM)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,8 +107,9 @@ func StartHeartbeatDetection() {
|
|||||||
|
|
||||||
// 发送心跳请求
|
// 发送心跳请求
|
||||||
func sendHeartbeat(client *rpc.Client) (bool, error) {
|
func sendHeartbeat(client *rpc.Client) (bool, error) {
|
||||||
|
|
||||||
// 1. 获取主机信息
|
// 1. 获取主机信息
|
||||||
hostInfoData := services.GetAllInfo()
|
hostInfoData := host_info.GetAllInfo()
|
||||||
hostInfo := models.HostInfo{
|
hostInfo := models.HostInfo{
|
||||||
SystemInfo: hostInfoData.SystemInfo,
|
SystemInfo: hostInfoData.SystemInfo,
|
||||||
CPUInfo: hostInfoData.CPUInfo,
|
CPUInfo: hostInfoData.CPUInfo,
|
||||||
@@ -98,26 +118,21 @@ func sendHeartbeat(client *rpc.Client) (bool, error) {
|
|||||||
NetInfo: hostInfoData.NetInfo,
|
NetInfo: hostInfoData.NetInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 获取应用名称
|
// 获取环境信息
|
||||||
appName := os.Getenv(appNameEnv)
|
envInfo := GetEnvInfo()
|
||||||
if appName == "" {
|
|
||||||
appName = "unknown-app"
|
|
||||||
log.Printf("警告: 环境变量 %s 未设置,使用默认值: %s", appNameEnv, appName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构建心跳请求
|
// 构建心跳请求
|
||||||
request := &models.HeartbeatRequest{
|
request := &models.HeartbeatRequest{
|
||||||
HostInfo: hostInfo,
|
HostInfo: hostInfo,
|
||||||
Timestamp: time.Now().Unix(),
|
Timestamp: time.Now().Unix(),
|
||||||
AppName: appName,
|
EnvInfo: envInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 如果已有TOTP密钥,则生成TOTP验证码
|
// 3. 如果已有TOTP密钥,则生成TOTP验证码
|
||||||
totpSecret := totp.GetTOTPSecret()
|
if tierTwoTotpSecret != "" {
|
||||||
if totpSecret != "" {
|
totpCode, err := totp_tier_two.GenerateTierTwoTOTPCode(tierTwoTotpSecret)
|
||||||
totpCode, err := totp.GenerateTOTPCode()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("生成TOTP验证码失败: %v", err)
|
wdd_log.Error("生成TOTP验证码失败: %v", err)
|
||||||
} else {
|
} else {
|
||||||
request.TOTPCode = totpCode
|
request.TOTPCode = totpCode
|
||||||
}
|
}
|
||||||
@@ -126,20 +141,24 @@ func sendHeartbeat(client *rpc.Client) (bool, error) {
|
|||||||
// 4. 发送心跳请求
|
// 4. 发送心跳请求
|
||||||
response, err := client.SendHeartbeatWithRetry(request, 10*time.Second)
|
response, err := client.SendHeartbeatWithRetry(request, 10*time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("发送心跳请求失败: %w", err)
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response == nil {
|
||||||
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 处理响应
|
// 5. 处理响应
|
||||||
if response.SecondTOTPSecret != "" {
|
if response.SecondTOTPSecret != "" {
|
||||||
// 存储TOTP密钥
|
// 存储TOTP密钥
|
||||||
totp.SetTOTPSecret(response.SecondTOTPSecret)
|
tierTwoTotpSecret = response.SecondTOTPSecret
|
||||||
log.Println("已更新TOTP密钥")
|
wdd_log.Info("已更新TOTP密钥 => %s", tierTwoTotpSecret)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. 如果有TOTP验证码,进行验证
|
// 6. 如果有TOTP验证码,进行验证
|
||||||
if response.TOTPCode != "" && totpSecret != "" {
|
if response.TOTPCode != "" && tierTwoTotpSecret != "" {
|
||||||
if !totp.ValidateTOTPCode(response.TOTPCode) {
|
if !totp_tier_two.VerifyTierTwoTOTPCode(response.TOTPCode, tierTwoTotpSecret) {
|
||||||
log.Println("TOTP验证码验证失败")
|
wdd_log.Warn("TOTP验证码验证失败")
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,38 +2,10 @@ module cmii-uav-watchdog-agent
|
|||||||
|
|
||||||
go 1.24
|
go 1.24
|
||||||
|
|
||||||
require (
|
require cmii-uav-watchdog-common v0.0.0
|
||||||
cmii-uav-watchdog-common v0.0.0
|
|
||||||
github.com/gin-gonic/gin v1.10.0
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
require cmii-uav-watchdog-otp v0.0.0 // indirect
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|
||||||
replace cmii-uav-watchdog-common => ../cmii-uav-watchdog-common
|
replace cmii-uav-watchdog-common => ../cmii-uav-watchdog-common
|
||||||
|
|
||||||
|
replace cmii-uav-watchdog-otp => ../cmii-uav-watchdog-otp
|
||||||
|
|||||||
@@ -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=
|
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package services
|
package host_info
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"cmii-uav-watchdog-common/models"
|
"cmii-uav-watchdog-common/models"
|
||||||
"fmt"
|
"cmii-uav-watchdog-common/wdd_log"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,151 +16,190 @@ func NewCPUInfo() models.CPUInfo {
|
|||||||
return models.CPUInfo{
|
return models.CPUInfo{
|
||||||
ModelName: "unknown",
|
ModelName: "unknown",
|
||||||
Cores: 0,
|
Cores: 0,
|
||||||
Architecture: "unknown",
|
Architecture: "amd64",
|
||||||
UUID: "unknown",
|
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 {
|
func GetCPUInfo() models.CPUInfo {
|
||||||
cpuInfo := NewCPUInfo()
|
wdd_log.Debug("开始获取CPU信息")
|
||||||
|
|
||||||
// 读取 /proc/cpuinfo
|
|
||||||
file, err := os.Open("/proc/cpuinfo")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error opening /proc/cpuinfo:", err)
|
|
||||||
return cpuInfo // 返回默认值
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := file.Close(); err != nil {
|
|
||||||
fmt.Println("Error closing /proc/cpuinfo:", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(file)
|
|
||||||
for scanner.Scan() {
|
|
||||||
parseLine(scanner.Text(), &cpuInfo)
|
|
||||||
}
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
fmt.Println("Error reading /proc/cpuinfo:", err)
|
|
||||||
return cpuInfo // 返回默认值
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取 CPU 核心数
|
|
||||||
cores, err := getCPUCores()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error getting CPU cores:", err)
|
|
||||||
} else {
|
|
||||||
cpuInfo.Cores = cores
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取 CPU UUID
|
|
||||||
uuid, err := getCPUUUID()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error getting CPU UUID:", err)
|
|
||||||
} else {
|
|
||||||
cpuInfo.UUID = uuid
|
|
||||||
}
|
|
||||||
|
|
||||||
cpuInfo.Architecture = getCPUArchitecture()
|
|
||||||
|
|
||||||
|
// 首先尝试从/proc/cpuinfo文件中读取信息
|
||||||
|
cpuInfo, err := getCPUInfoFromProc()
|
||||||
|
if err == nil {
|
||||||
|
wdd_log.Debug("成功从/proc/cpuinfo获取CPU信息")
|
||||||
return cpuInfo
|
return cpuInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// 如果从文件获取失败,尝试使用lscpu命令
|
||||||
CPU模型名称: Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz
|
wdd_log.Warn("从/proc/cpuinfo获取CPU信息失败: %s,尝试使用lscpu命令", err.Error())
|
||||||
核心数量: 4
|
cpuInfo, err = getCPUInfoFromLscpu()
|
||||||
架构信息: x86_64
|
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文件
|
||||||
|
file, err := os.Open("/proc/cpuinfo")
|
||||||
|
if err != nil {
|
||||||
|
return cpuInfo, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// 使用scanner读取文件
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
|
||||||
|
// 处理的CPU核心数量
|
||||||
|
coreCount := 1
|
||||||
|
var flags []string
|
||||||
|
|
||||||
|
// 逐行读取并解析
|
||||||
|
for scanner.Scan() {
|
||||||
|
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字段获取到核心数,使用处理器计数
|
||||||
|
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 {
|
||||||
|
return cpuInfo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析输出
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package services
|
package host_info
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmii-uav-watchdog-common/models"
|
"cmii-uav-watchdog-common/models"
|
||||||
"fmt"
|
"cmii-uav-watchdog-common/wdd_log"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
@@ -68,7 +68,7 @@ func GetDiskInfo() []models.DiskInfo {
|
|||||||
// Get disk usage information using stat
|
// Get disk usage information using stat
|
||||||
var stat syscall.Statfs_t
|
var stat syscall.Statfs_t
|
||||||
if err := syscall.Statfs(mountPoint, &stat); err != nil {
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +85,8 @@ func GetDiskInfo() []models.DiskInfo {
|
|||||||
// Calculate percentage used
|
// Calculate percentage used
|
||||||
usePercent := "0%"
|
usePercent := "0%"
|
||||||
if total > 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
|
// Determine the physical device and its size
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package services
|
package host_info
|
||||||
|
|
||||||
import "cmii-uav-watchdog-common/models"
|
import "cmii-uav-watchdog-common/models"
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package services
|
package host_info
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmii-uav-watchdog-common/models"
|
"cmii-uav-watchdog-common/models"
|
||||||
"fmt"
|
"cmii-uav-watchdog-common/wdd_log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -19,7 +19,7 @@ var DefaultMotherboardInfo = models.MotherboardInfo{
|
|||||||
func readFileWithWarning(path string) ([]byte, error) {
|
func readFileWithWarning(path string) ([]byte, error) {
|
||||||
value, err := os.ReadFile(path)
|
value, err := os.ReadFile(path)
|
||||||
if err != nil {
|
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 nil, err
|
||||||
}
|
}
|
||||||
return value, nil
|
return value, nil
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package services
|
package host_info
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmii-uav-watchdog-common/models"
|
"cmii-uav-watchdog-common/models"
|
||||||
"fmt"
|
"cmii-uav-watchdog-common/wdd_log"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -26,7 +26,7 @@ func GetMemoryInfo() models.MemoryInfo {
|
|||||||
memInfo := NewMemoryInfo()
|
memInfo := NewMemoryInfo()
|
||||||
data, err := os.ReadFile("/proc/meminfo")
|
data, err := os.ReadFile("/proc/meminfo")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error reading /proc/meminfo:", err)
|
wdd_log.Error("读取 /proc/meminfo 失败: %v", err)
|
||||||
return memInfo
|
return memInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ func GetMemoryInfo() models.MemoryInfo {
|
|||||||
key := fields[0]
|
key := fields[0]
|
||||||
value, err := strconv.ParseUint(fields[1], 10, 64)
|
value, err := strconv.ParseUint(fields[1], 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error parsing value for %s: %v\n", key, err)
|
wdd_log.Error("解析 %s 的值失败: %v", key, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package services
|
package host_info
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmii-uav-watchdog-common/models"
|
"cmii-uav-watchdog-common/models"
|
||||||
"fmt"
|
"cmii-uav-watchdog-common/wdd_log"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -25,7 +25,7 @@ func GetNetworkInterfaces() []models.NetworkInterfaceInfo {
|
|||||||
// 获取所有网络接口
|
// 获取所有网络接口
|
||||||
ifaces, err := net.Interfaces()
|
ifaces, err := net.Interfaces()
|
||||||
if err != nil {
|
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{}}}
|
return []models.NetworkInterfaceInfo{{Name: "unknown", MACAddress: "00:00:00:00:00:00", IPAddresses: []string{}}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ func GetNetworkInterfaces() []models.NetworkInterfaceInfo {
|
|||||||
// 获取 MAC 地址
|
// 获取 MAC 地址
|
||||||
macAddress, err := getMACAddress(iface)
|
macAddress, err := getMACAddress(iface)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error getting MAC address for", iface.Name, ":", err)
|
wdd_log.Error("获取网卡 %s 的MAC地址失败: %v", iface.Name, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ func GetNetworkInterfaces() []models.NetworkInterfaceInfo {
|
|||||||
var ipAddresses []string
|
var ipAddresses []string
|
||||||
addrs, err := iface.Addrs()
|
addrs, err := iface.Addrs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error getting addresses for", iface.Name, ":", err)
|
wdd_log.Error("获取网卡 %s 的IP地址失败: %v", iface.Name, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package services
|
package host_info
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmii-uav-watchdog-common/models"
|
"cmii-uav-watchdog-common/models"
|
||||||
"log"
|
"cmii-uav-watchdog-common/wdd_log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -16,10 +16,6 @@ func NewOSInfo() models.OSInfo {
|
|||||||
IDLike: "unknown",
|
IDLike: "unknown",
|
||||||
VersionID: "unknown",
|
VersionID: "unknown",
|
||||||
PrettyName: "unknown",
|
PrettyName: "unknown",
|
||||||
HomeURL: "unknown",
|
|
||||||
SupportURL: "unknown",
|
|
||||||
BugReportURL: "unknown",
|
|
||||||
PrivacyURL: "unknown",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +37,14 @@ func GetSystemInfo() models.SystemInfo {
|
|||||||
if machineID != "" {
|
if machineID != "" {
|
||||||
sysInfo.MachineID = machineID
|
sysInfo.MachineID = machineID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取 machine serial
|
||||||
|
|
||||||
|
serialID := getMachineSerial()
|
||||||
|
if serialID != "" {
|
||||||
|
sysInfo.MachineSerial = serialID
|
||||||
|
}
|
||||||
|
|
||||||
// 获取操作系统版本
|
// 获取操作系统版本
|
||||||
sysInfo.OS = getOSVersion()
|
sysInfo.OS = getOSVersion()
|
||||||
|
|
||||||
@@ -50,12 +54,26 @@ func GetSystemInfo() models.SystemInfo {
|
|||||||
sysInfo.KernelVersion = kernelVersion
|
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
|
return sysInfo
|
||||||
}
|
}
|
||||||
func getOSVersion() models.OSInfo {
|
func getOSVersion() models.OSInfo {
|
||||||
data, err := os.ReadFile("/etc/os-release")
|
data, err := os.ReadFile("/etc/os-release")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error reading /etc/os-release: %v", err)
|
wdd_log.Error("读取 /etc/os-release 失败: %v", err)
|
||||||
return NewOSInfo() // 返回默认值
|
return NewOSInfo() // 返回默认值
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,25 +106,27 @@ func getOSVersion() models.OSInfo {
|
|||||||
osInfo.VersionID = value
|
osInfo.VersionID = value
|
||||||
case "PRETTY_NAME":
|
case "PRETTY_NAME":
|
||||||
osInfo.PrettyName = value
|
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
|
return osInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// getMachineID 从 /etc/machine-id 文件中获取机器 ID
|
// getMachineID 从 /sys/devices/virtual/dmi/id/product_uuid 文件中获取机器 ID
|
||||||
func getMachineID() string {
|
func getMachineID() string {
|
||||||
data, err := os.ReadFile("/etc/machine-id")
|
data, err := os.ReadFile("/sys/devices/virtual/dmi/id/product_uuid")
|
||||||
if err != nil {
|
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 ""
|
||||||
}
|
}
|
||||||
return strings.TrimSpace(string(data)) // 去除多余空格
|
return strings.TrimSpace(string(data)) // 去除多余空格
|
||||||
@@ -116,8 +136,8 @@ func getMachineID() string {
|
|||||||
func getKernelVersion() string {
|
func getKernelVersion() string {
|
||||||
data, err := os.ReadFile("/proc/version")
|
data, err := os.ReadFile("/proc/version")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error reading /proc/version: %v", err)
|
wdd_log.Error("读取 /proc/version 失败: %v", err)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return string(data)
|
return strings.TrimSpace(string(data))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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...")
|
|
||||||
}
|
|
||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"cmii-uav-watchdog-common/models"
|
"cmii-uav-watchdog-common/models"
|
||||||
@@ -23,7 +24,8 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
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客户端配置选项
|
// ClientOptions HTTP客户端配置选项
|
||||||
@@ -50,6 +52,7 @@ func DefaultClientOptions() *ClientOptions {
|
|||||||
type Client struct {
|
type Client struct {
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
options *ClientOptions
|
options *ClientOptions
|
||||||
|
heartbeatURL string // 心跳请求的URL地址
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient 创建一个新的HTTP客户端
|
// NewClient 创建一个新的HTTP客户端
|
||||||
@@ -58,7 +61,7 @@ type Client struct {
|
|||||||
//
|
//
|
||||||
// 返回:
|
// 返回:
|
||||||
// - *Client: HTTP客户端实例
|
// - *Client: HTTP客户端实例
|
||||||
func NewClient(options *ClientOptions) *Client {
|
func NewClient(options *ClientOptions, heartbeatURL string) *Client {
|
||||||
if options == nil {
|
if options == nil {
|
||||||
options = DefaultClientOptions()
|
options = DefaultClientOptions()
|
||||||
}
|
}
|
||||||
@@ -75,12 +78,24 @@ func NewClient(options *ClientOptions) *Client {
|
|||||||
Transport: transport,
|
Transport: transport,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 解析heartbeatURL
|
||||||
|
heartbeatURL = strings.TrimSpace(heartbeatURL)
|
||||||
|
if heartbeatURL == "" {
|
||||||
|
heartbeatURL = DefaultHeartbeatURL
|
||||||
|
}
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
httpClient: httpClient,
|
httpClient: httpClient,
|
||||||
options: options,
|
options: options,
|
||||||
|
heartbeatURL: heartbeatURL,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetHeartbeatURL 获取心跳请求的URL地址
|
||||||
|
func (c *Client) GetHeartbeatURL() string {
|
||||||
|
return c.heartbeatURL
|
||||||
|
}
|
||||||
|
|
||||||
// SendHeartbeat 发送心跳请求并处理响应
|
// SendHeartbeat 发送心跳请求并处理响应
|
||||||
// 参数:
|
// 参数:
|
||||||
// - ctx: 上下文,用于取消请求
|
// - ctx: 上下文,用于取消请求
|
||||||
@@ -114,7 +129,7 @@ func (c *Client) SendHeartbeat(ctx context.Context, request *models.HeartbeatReq
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建HTTP请求
|
// 创建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 {
|
if err != nil {
|
||||||
lastError = fmt.Errorf("创建请求失败: %w", err)
|
lastError = fmt.Errorf("创建请求失败: %w", err)
|
||||||
continue
|
continue
|
||||||
@@ -140,9 +155,37 @@ func (c *Client) SendHeartbeat(ctx context.Context, request *models.HeartbeatReq
|
|||||||
// 确保响应体被关闭
|
// 确保响应体被关闭
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
// 检查HTTP状态码
|
// 使用switch检查HTTP状态码
|
||||||
if resp.StatusCode != http.StatusOK {
|
switch resp.StatusCode {
|
||||||
lastError = fmt.Errorf("%w: 状态码 %d", ErrInvalidStatusCode, 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)
|
_, _ = io.Copy(io.Discard, resp.Body)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
37
cmii-uav-watchdog-center/cmd/main.go
Normal file
37
cmii-uav-watchdog-center/cmd/main.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
11
cmii-uav-watchdog-center/config/config.go
Normal file
11
cmii-uav-watchdog-center/config/config.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
// Config 配置模型
|
||||||
|
type Config struct {
|
||||||
|
Server ServerConfig `json:"server"` // 服务器配置
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerConfig 服务器配置
|
||||||
|
type ServerConfig struct {
|
||||||
|
Port int `json:"port"` // 服务器端口
|
||||||
|
}
|
||||||
74
cmii-uav-watchdog-center/config/config_loader.go
Normal file
74
cmii-uav-watchdog-center/config/config_loader.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
38
cmii-uav-watchdog-center/controllers/auth_controller.go
Normal file
38
cmii-uav-watchdog-center/controllers/auth_controller.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
45
cmii-uav-watchdog-center/controllers/project_controller.go
Normal file
45
cmii-uav-watchdog-center/controllers/project_controller.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
@@ -1,3 +1,40 @@
|
|||||||
module cmii-uav-watchdog-center
|
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
|
||||||
|
|||||||
86
cmii-uav-watchdog-center/go.sum
Normal file
86
cmii-uav-watchdog-center/go.sum
Normal file
@@ -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=
|
||||||
36
cmii-uav-watchdog-center/router/router.go
Normal file
36
cmii-uav-watchdog-center/router/router.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
64
cmii-uav-watchdog-center/services/auth_service.go
Normal file
64
cmii-uav-watchdog-center/services/auth_service.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
101
cmii-uav-watchdog-center/services/project_service.go
Normal file
101
cmii-uav-watchdog-center/services/project_service.go
Normal file
@@ -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("项目不存在")
|
||||||
|
}
|
||||||
18
cmii-uav-watchdog-center/services/totp_service.go
Normal file
18
cmii-uav-watchdog-center/services/totp_service.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
module cmii-uav-watchdog-common
|
module cmii-uav-watchdog-common
|
||||||
|
|
||||||
go 1.24
|
go 1.24
|
||||||
|
|
||||||
|
require cmii-uav-watchdog-otp v0.0.0
|
||||||
|
|
||||||
|
replace cmii-uav-watchdog-otp => ../cmii-uav-watchdog-otp
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
//TIP <p>To run your code, right-click the code and select <b>Run</b>.</p> <p>Alternatively, click
|
|
||||||
// the <icon src="AllIcons.Actions.Execute"/> icon in the gutter and select the <b>Run</b> menu item from here.</p>
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
//TIP <p>Press <shortcut actionId="ShowIntentionActions"/> when your caret is at the underlined text
|
|
||||||
// to see how GoLand suggests fixing the warning.</p><p>Alternatively, if available, click the lightbulb to view possible fixes.</p>
|
|
||||||
s := "gopher"
|
|
||||||
fmt.Println("Hello and welcome, %s!", s)
|
|
||||||
|
|
||||||
for i := 1; i <= 5; i++ {
|
|
||||||
//TIP <p>To start your debugging session, right-click your code in the editor and select the Debug option.</p> <p>We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
|
|
||||||
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.</p>
|
|
||||||
fmt.Println("i =", 100/i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +1,32 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// AuthorizationFile 授权文件模型
|
// AuthorizationFile 授权文件模型
|
||||||
type AuthorizationFile struct {
|
type AuthorizationFile struct {
|
||||||
EncryptedHosts []string `json:"encrypted_hosts"` // 加密后的主机信息列表
|
EncryptedHostMap map[string]HostInfo `json:"encrypted_host_map"` // 加密后的主机信息列表
|
||||||
TOTPCode string `json:"totp_code"` // TOTP验证码
|
TOTPCode string `json:"totp_code"` // TOTP验证码
|
||||||
CurrentTime time.Time `json:"current_time"` // 当前系统时间
|
CurrentTime string `json:"current_time"` // 当前系统时间
|
||||||
FirstAuthTime time.Time `json:"first_auth_time"` // 初次授权时间
|
FirstAuthTime string `json:"first_auth_time"` // 初次授权时间
|
||||||
TimeOffset int64 `json:"time_offset"` // 授权时间偏移
|
TimeOffset int64 `json:"time_offset"` // 授权时间偏移
|
||||||
|
ProjectNamespace string `json:"project_namespace"` // 项目命名空间
|
||||||
|
EncryptedNamespace string `json:"encrypted_namespace"` // 加密后的项目命名空间 防止信息篡改
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthorizationCode 授权码模型
|
// AuthorizationCode 授权码模型
|
||||||
type AuthorizationCode struct {
|
type AuthorizationCode struct {
|
||||||
TOTPCode string `json:"totp_code"` // TOTP验证码
|
TOTPCode string `json:"totp_code"` // TOTP验证码
|
||||||
CurrentTime time.Time `json:"current_time"` // 当前系统时间
|
CurrentTime string `json:"current_time"` // 当前系统时间
|
||||||
EncryptedHosts []string `json:"encrypted_hosts"` // 授权主机的加密字符串列表
|
EncryptedHostMap map[string]HostInfo `json:"encrypted_host_map"` // 加密后的主机信息列表 防止信息篡改
|
||||||
|
ProjectNamespace string `json:"project_namespace"` // 项目命名空间
|
||||||
|
EncryptedNamespace string `json:"encrypted_namespace"` // 加密后的项目命名空间 防止信息篡改
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthorizationStorage 授权存储信息
|
// AuthorizationStorage 授权存储信息
|
||||||
type AuthorizationStorage struct {
|
type AuthorizationStorage struct {
|
||||||
EncryptedCode string `json:"encrypted_code"` // 加密后的授权码
|
EncryptedAuthrizationCode string `json:"encrypted_authrization_code"` // 加密后的授权码,防止信息篡改
|
||||||
FirstAuthTime time.Time `json:"first_auth_time"` // 初次授权时间
|
FirstAuthTime string `json:"first_auth_time"` // 初次授权时间
|
||||||
TimeOffset int64 `json:"time_offset"` // 授权时间偏移
|
TimeOffset int64 `json:"time_offset"` // 授权时间偏移
|
||||||
AuthorizedHosts []string `json:"authorized_hosts"` // 已授权主机列表
|
AuthorizedHostMap map[string]HostInfo `json:"authorized_host_map"` // 已授权主机列表
|
||||||
SecondTOTPSecret string `json:"second_totp_secret"` // 第二级的totp密钥
|
SecondTOTPSecret string `json:"second_totp_secret"` // 第二级的totp密钥
|
||||||
|
ProjectNamespace string `json:"project_namespace"` // 项目命名空间
|
||||||
|
EncryptedNamespace string `json:"encrypted_namespace"` // 加密后的项目命名空间 防止信息篡改
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ type CPUInfo struct {
|
|||||||
ModelName string `json:"model_name"`
|
ModelName string `json:"model_name"`
|
||||||
Cores int `json:"cores"`
|
Cores int `json:"cores"`
|
||||||
Architecture string `json:"architecture"`
|
Architecture string `json:"architecture"`
|
||||||
UUID string `json:"uuid"`
|
Flags []string `json:"flags"`
|
||||||
|
Hypervisor string `json:"hypervisor"`
|
||||||
|
Virtualization string `json:"virtualization"`
|
||||||
|
Frequency string `json:"frequency"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MemoryInfo holds the memory information.
|
// MemoryInfo holds the memory information.
|
||||||
@@ -49,9 +52,12 @@ type MotherboardInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SystemInfo struct {
|
type SystemInfo struct {
|
||||||
MachineID string `json:"machine_id"` // 唯一标识符
|
MachineID string `json:"machine_id"` // 主机唯一标识符
|
||||||
|
MachineSerial string `json:"machine_serial"` // 主机序列号
|
||||||
OS OSInfo `json:"os"` // 操作系统
|
OS OSInfo `json:"os"` // 操作系统
|
||||||
KernelVersion string `json:"kernel_version"` // 内核版本
|
KernelVersion string `json:"kernel_version"` // 内核版本
|
||||||
|
NodeName string `json:"node_name"` // 节点名称
|
||||||
|
NodeIP string `json:"node_ip"` // 节点IP
|
||||||
}
|
}
|
||||||
|
|
||||||
type OSInfo struct {
|
type OSInfo struct {
|
||||||
@@ -61,10 +67,6 @@ type OSInfo struct {
|
|||||||
IDLike string `json:"id_like"`
|
IDLike string `json:"id_like"`
|
||||||
VersionID string `json:"version_id"`
|
VersionID string `json:"version_id"`
|
||||||
PrettyName string `json:"pretty_name"`
|
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"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HostInfo 主机信息模型
|
// HostInfo 主机信息模型
|
||||||
@@ -77,12 +79,32 @@ type HostInfo struct {
|
|||||||
MotherboardInfo MotherboardInfo `json:"motherboard_info"`
|
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 心跳请求
|
// HeartbeatRequest 心跳请求
|
||||||
type HeartbeatRequest struct {
|
type HeartbeatRequest struct {
|
||||||
HostInfo HostInfo `json:"host_info"` // 主机信息
|
HostInfo HostInfo `json:"host_info"` // 主机信息
|
||||||
|
EnvInfo EnvInfo `json:"env_info"` // 环境信息
|
||||||
Timestamp int64 `json:"timestamp"` // 时间戳
|
Timestamp int64 `json:"timestamp"` // 时间戳
|
||||||
TOTPCode string `json:"totp_code"` // TOTP验证码
|
TOTPCode string `json:"totp_code"` // TOTP验证码
|
||||||
AppName string `json:"app_name"` // 应用名称
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HeartbeatResponse 心跳响应
|
// HeartbeatResponse 心跳响应
|
||||||
|
|||||||
22
cmii-uav-watchdog-common/models/project.go
Normal file
22
cmii-uav-watchdog-common/models/project.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
169
cmii-uav-watchdog-common/totp_tier_one/crypto.go
Normal file
169
cmii-uav-watchdog-common/totp_tier_one/crypto.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
218
cmii-uav-watchdog-common/totp_tier_one/crypto_test.go
Normal file
218
cmii-uav-watchdog-common/totp_tier_one/crypto_test.go
Normal file
@@ -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("解密后的主机信息与修改后的主机信息相同, 不应该相同")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
36
cmii-uav-watchdog-common/utils/time_utils.go
Normal file
36
cmii-uav-watchdog-common/utils/time_utils.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
150
cmii-uav-watchdog-common/wdd_log/log_utils.go
Normal file
150
cmii-uav-watchdog-common/wdd_log/log_utils.go
Normal file
@@ -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...)
|
||||||
|
}
|
||||||
@@ -34,7 +34,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const debug = true
|
const debug = false
|
||||||
|
|
||||||
// Validate a HOTP passcode given a counter and secret.
|
// Validate a HOTP passcode given a counter and secret.
|
||||||
// This is a shortcut for ValidateCustom, with parameters that
|
// This is a shortcut for ValidateCustom, with parameters that
|
||||||
|
|||||||
@@ -1,24 +1,42 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cmii-uav-watchdog-common/wdd_log"
|
||||||
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
localAuthFilePath = "/cmii/cmii-uav-watchdog/auth_code.json"
|
||||||
|
configFilePath = "/cmii/cmii-uav-watchdog/config.yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config 配置结构体
|
// Config 配置结构体
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Server struct {
|
Server struct {
|
||||||
Port string `mapstructure:"port"`
|
Port string `mapstructure:"port"`
|
||||||
|
Debug bool `mapstructure:"debug"`
|
||||||
} `mapstructure:"server"`
|
} `mapstructure:"server"`
|
||||||
|
|
||||||
Auth struct {
|
TierOneAuth struct {
|
||||||
Secret string `mapstructure:"secret"` // TOTP密钥
|
TierOneSecret string `mapstructure:"tier_one_secret" yaml:"tier_one_secret"` // TOTP密钥
|
||||||
AuthFilePath string `mapstructure:"auth_file"` // 授权文件路径
|
TimeOffsetAllowed int64 `mapstructure:"time_offset_allowed" yaml:"time_offset_allowed"` // 允许的时间偏移(秒)
|
||||||
TimeOffsetAllowed int64 `mapstructure:"time_offset_allowed"` // 允许的时间偏移(秒)
|
LocalAuthFilePath string `mapstructure:"local_auth_file_path" yaml:"local_auth_file_path"` // 本地授权文件路径
|
||||||
} `mapstructure:"auth"`
|
} `mapstructure:"tier_one_auth" yaml:"tier_one_auth"`
|
||||||
|
|
||||||
WatchdogCenter struct {
|
WatchdogCenter struct {
|
||||||
URL string `mapstructure:"url"` // 一级授权中心地址
|
URL string `mapstructure:"url" yaml:"url"` // 一级授权中心地址
|
||||||
} `mapstructure:"watchdog_center"`
|
} `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 (
|
var (
|
||||||
@@ -27,22 +45,56 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// LoadConfig 加载配置
|
// LoadConfig 加载配置
|
||||||
func LoadConfig(path string) error {
|
// 单例模式, 如果已经初始化过, 则直接返回
|
||||||
once.Do(func() {
|
// 如果config文件不存在,报错 无法启动
|
||||||
// 设置默认配置值
|
// 不使用viper,用最简单的方式读取配置文件,解析其中的配置
|
||||||
config.Server.Port = "8080"
|
// 如果解析失败,无法得到config 则报错 无法启动
|
||||||
config.Auth.Secret = "default-secret-key-please-change-in-production"
|
func LoadConfig() error {
|
||||||
config.Auth.AuthFilePath = "./auth-storage.json"
|
|
||||||
config.Auth.TimeOffsetAllowed = 3600 // 默认允许1小时的时间偏移
|
var err error
|
||||||
config.WatchdogCenter.URL = "http://localhost:8081"
|
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 获取配置
|
// GetConfig 获取配置
|
||||||
func GetConfig() *Config {
|
func GetConfig() *Config {
|
||||||
return &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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
server:
|
server:
|
||||||
port: "8080"
|
port: "8080" # 服务器端口
|
||||||
|
debug: true
|
||||||
|
|
||||||
auth:
|
tier_one_auth:
|
||||||
secret: "your-secret-key-for-totp"
|
tier_one_secret: "NK537TIWSUOFIS7SYCUJ6A7FPOGFVM3UH67TJRX3IYQAHKZXK2X7SBAA6JOXZVSV3U6K5YZUX7Q6TWOPK6YCRU6MIML33ZJFBN55I2Q" # TOTP密钥
|
||||||
auth_file: "./data/auth.json"
|
time_offset_allowed: 30 # 允许的时间偏移(秒)
|
||||||
time_offset_allowed: 3600 # 允许的时间偏移,单位秒
|
|
||||||
|
|
||||||
watchdog_center:
|
watchdog_center:
|
||||||
url: "http://center-service:8080/api"
|
url: "https://watchdog-center.example.com" # 一级授权中心地址
|
||||||
|
|
||||||
|
project:
|
||||||
|
project_namespace: "uavcloud-devflight" # 项目命名空间
|
||||||
|
|
||||||
|
tier_two_auth:
|
||||||
|
tier_two_secret: "your_tier_two_secret_here" # 二级授权密钥
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package controllers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
models2 "cmii-uav-watchdog-common/models"
|
models2 "cmii-uav-watchdog-common/models"
|
||||||
"cmii-uav-watchdog/models"
|
"cmii-uav-watchdog-common/wdd_log"
|
||||||
"cmii-uav-watchdog/services"
|
"cmii-uav-watchdog/services"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
@@ -26,59 +26,52 @@ func (ac *AuthController) GenerateAuthFile(c *gin.Context) {
|
|||||||
// 生成授权文件
|
// 生成授权文件
|
||||||
authFile, err := ac.authService.GenerateAuthorizationFile()
|
authFile, err := ac.authService.GenerateAuthorizationFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, models.Response{
|
c.JSON(http.StatusInternalServerError, authFile)
|
||||||
Code: 500,
|
|
||||||
Message: "生成授权文件失败",
|
|
||||||
Data: nil,
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, models.Response{
|
c.JSON(http.StatusOK, authFile)
|
||||||
Code: 200,
|
|
||||||
Message: "生成授权文件成功",
|
|
||||||
Data: authFile,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReceiveAuthCode 接收授权码
|
// ReceiveAuthCode 接收授权码
|
||||||
func (ac *AuthController) ReceiveAuthCode(c *gin.Context) {
|
func (ac *AuthController) ReceiveAuthCode(c *gin.Context) {
|
||||||
var authCode models2.AuthorizationCode
|
var authCode models2.AuthorizationCode
|
||||||
if err := c.ShouldBindJSON(&authCode); err != nil {
|
if err := c.ShouldBindJSON(&authCode); err != nil {
|
||||||
c.JSON(http.StatusBadRequest, models.Response{
|
c.JSON(http.StatusBadRequest, authCode)
|
||||||
Code: 400,
|
|
||||||
Message: "无效的请求参数",
|
|
||||||
Data: nil,
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理授权码
|
// 处理授权码
|
||||||
err := ac.authService.ProcessAuthorizationCode(authCode)
|
err := ac.authService.ProcessAuthorizationCode(authCode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, models.Response{
|
wdd_log.Error("处理授权码失败: %v", err)
|
||||||
Code: 500,
|
c.JSON(http.StatusInternalServerError, authCode)
|
||||||
Message: "处理授权码失败: " + err.Error(),
|
|
||||||
Data: nil,
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, models.Response{
|
c.JSON(http.StatusOK, authCode)
|
||||||
Code: 200,
|
|
||||||
Message: "处理授权码成功",
|
|
||||||
Data: nil,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotifyAuthInfo 通知授权信息
|
// NotifyAuthInfo 通知授权信息
|
||||||
func (ac *AuthController) NotifyAuthInfo(c *gin.Context) {
|
func (ac *AuthController) NotifyAuthInfo(c *gin.Context) {
|
||||||
// 获取授权信息
|
// 获取授权信息
|
||||||
authInfo := ac.authService.GetAuthorizationInfo()
|
authInfo, err := ac.authService.GetAuthorizationInfo()
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, models.Response{
|
c.JSON(http.StatusOK, authInfo)
|
||||||
Code: 200,
|
}
|
||||||
Message: "获取授权信息成功",
|
|
||||||
Data: 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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
24
cmii-uav-watchdog/controllers/cmii_controller.go
Normal file
24
cmii-uav-watchdog/controllers/cmii_controller.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ package controllers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
models2 "cmii-uav-watchdog-common/models"
|
models2 "cmii-uav-watchdog-common/models"
|
||||||
"cmii-uav-watchdog/models"
|
"cmii-uav-watchdog-common/wdd_log"
|
||||||
"cmii-uav-watchdog/services"
|
"cmii-uav-watchdog/services"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
@@ -25,28 +25,36 @@ func NewHeartbeatController() *HeartbeatController {
|
|||||||
func (hc *HeartbeatController) HandleHeartbeat(c *gin.Context) {
|
func (hc *HeartbeatController) HandleHeartbeat(c *gin.Context) {
|
||||||
var req models2.HeartbeatRequest
|
var req models2.HeartbeatRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
c.JSON(http.StatusBadRequest, models.Response{
|
c.JSON(http.StatusBadRequest, req)
|
||||||
Code: 400,
|
|
||||||
Message: "无效的请求参数",
|
|
||||||
Data: nil,
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理心跳
|
// 处理心跳
|
||||||
resp, err := hc.heartbeatService.ProcessHeartbeat(req)
|
resp, err := hc.heartbeatService.ProcessHeartbeat(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, models.Response{
|
c.JSON(http.StatusInternalServerError, resp)
|
||||||
Code: 500,
|
return
|
||||||
Message: "处理心跳失败: " + err.Error(),
|
}
|
||||||
Data: nil,
|
|
||||||
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, models.Response{
|
// 返回主机信息
|
||||||
Code: 200,
|
c.JSON(http.StatusOK, gin.H{
|
||||||
Message: "处理心跳成功",
|
"host_count": len(hostInfoMap),
|
||||||
Data: resp,
|
"hosts": hostInfoMap,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,15 @@ module cmii-uav-watchdog
|
|||||||
|
|
||||||
go 1.24
|
go 1.24
|
||||||
|
|
||||||
toolchain go1.24.1
|
toolchain go1.24.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cmii-uav-watchdog-common v0.0.0
|
cmii-uav-watchdog-common v0.0.0
|
||||||
cmii-uav-watchdog-otp v0.0.0
|
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
cmii-uav-watchdog-otp v0.0.0 // indirect
|
||||||
github.com/bytedance/sonic v1.9.1 // indirect
|
github.com/bytedance/sonic v1.9.1 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
|
|||||||
@@ -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.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 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
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-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 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
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.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.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 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
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 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
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 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
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 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
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 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/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 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
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/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/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
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/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 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
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.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 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
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 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
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 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
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-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 h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
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/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 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
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.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.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.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
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.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.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.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.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
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/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 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
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 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
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.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 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
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 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
||||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
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 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
||||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
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-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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
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 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
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/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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.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 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
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 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.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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
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/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=
|
|
||||||
|
|||||||
@@ -1,26 +1,25 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cmii-uav-watchdog-common/wdd_log"
|
||||||
"cmii-uav-watchdog/config"
|
"cmii-uav-watchdog/config"
|
||||||
"cmii-uav-watchdog/routes"
|
"cmii-uav-watchdog/routes"
|
||||||
"cmii-uav-watchdog/services"
|
"cmii-uav-watchdog/services"
|
||||||
"log"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
// 初始化配置信息
|
// 初始化配置信息
|
||||||
err := config.LoadConfig("./config/config.yaml")
|
err := config.LoadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("加载配置文件失败: %v", err)
|
panic(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化授权服务(使用单例模式)
|
// 初始化授权服务(使用单例模式)
|
||||||
authService := services.NewAuthService()
|
authService := services.NewAuthService()
|
||||||
if authService == nil {
|
if authService == nil {
|
||||||
log.Fatalf("初始化授权服务失败")
|
wdd_log.Fatal("初始化授权服务失败")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,11 +39,11 @@ func main() {
|
|||||||
// 启动服务
|
// 启动服务
|
||||||
port := config.GetConfig().Server.Port
|
port := config.GetConfig().Server.Port
|
||||||
if port == "" {
|
if port == "" {
|
||||||
port = "8080"
|
port = "8990"
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("服务启动在 :%s", port)
|
wdd_log.Info("服务启动在 :%s", port)
|
||||||
if err := r.Run(":" + port); err != nil {
|
if err := r.Run(":" + port); err != nil {
|
||||||
log.Fatalf("服务启动失败: %v", err)
|
wdd_log.Error("服务启动失败: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
// Response 通用响应模型
|
|
||||||
type Response struct {
|
|
||||||
Code int `json:"code"` // 状态码
|
|
||||||
Message string `json:"message"` // 响应消息
|
|
||||||
Data interface{} `json:"data"` // 响应数据
|
|
||||||
}
|
|
||||||
@@ -21,9 +21,10 @@ func SetupRouter() *gin.Engine {
|
|||||||
auth := api.Group("/authorization")
|
auth := api.Group("/authorization")
|
||||||
{
|
{
|
||||||
authController := controllers.NewAuthController()
|
authController := controllers.NewAuthController()
|
||||||
auth.POST("/generate", authController.GenerateAuthFile) // 授权文件生成
|
auth.GET("/generate", authController.GenerateAuthFile) // 授权文件生成
|
||||||
auth.POST("/code", authController.ReceiveAuthCode) // 授权码接收
|
auth.POST("/auth", authController.ReceiveAuthCode) // 授权码接收
|
||||||
auth.POST("/notify", authController.NotifyAuthInfo) // 授权信息通知
|
auth.POST("/info", authController.NotifyAuthInfo) // 授权信息通知
|
||||||
|
auth.GET("/hosts", authController.GetAllAuthorizedHosts) // 获取所有已授权主机信息
|
||||||
}
|
}
|
||||||
|
|
||||||
// 心跳检测路由
|
// 心跳检测路由
|
||||||
@@ -31,6 +32,14 @@ func SetupRouter() *gin.Engine {
|
|||||||
{
|
{
|
||||||
heartbeatController := controllers.NewHeartbeatController()
|
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) // 处理主机信息
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,15 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
models2 "cmii-uav-watchdog-common/models"
|
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/config"
|
||||||
"cmii-uav-watchdog/utils"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// 单例相关变量
|
// 单例相关变量
|
||||||
@@ -22,8 +23,8 @@ var (
|
|||||||
// AuthService 授权服务
|
// AuthService 授权服务
|
||||||
type AuthService struct {
|
type AuthService struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
hostInfoSet map[string]models2.HostInfo // 主机信息集合
|
heartBeatHostInfoMap map[string]models2.HostInfo // 主机信息集合
|
||||||
authorizationInfo models2.AuthorizationStorage // 授权信息
|
authorizationInfo *models2.AuthorizationStorage // 授权信息
|
||||||
totpService *TOTPService
|
totpService *TOTPService
|
||||||
initialized bool
|
initialized bool
|
||||||
}
|
}
|
||||||
@@ -42,7 +43,7 @@ func NewAuthService() *AuthService {
|
|||||||
|
|
||||||
// 创建新实例
|
// 创建新实例
|
||||||
service := &AuthService{
|
service := &AuthService{
|
||||||
hostInfoSet: make(map[string]models2.HostInfo),
|
heartBeatHostInfoMap: make(map[string]models2.HostInfo),
|
||||||
totpService: NewTOTPService(),
|
totpService: NewTOTPService(),
|
||||||
initialized: false,
|
initialized: false,
|
||||||
}
|
}
|
||||||
@@ -50,12 +51,17 @@ func NewAuthService() *AuthService {
|
|||||||
// 尝试从本地加载授权信息
|
// 尝试从本地加载授权信息
|
||||||
service.loadAuthorizationInfo()
|
service.loadAuthorizationInfo()
|
||||||
|
|
||||||
|
// 确保 authorizationInfo 不为 nil
|
||||||
|
if service.authorizationInfo == nil {
|
||||||
|
service.authorizationInfo = &models2.AuthorizationStorage{}
|
||||||
|
}
|
||||||
|
|
||||||
// 判断 项目级别的 TOTP密钥是否为空
|
// 判断 项目级别的 TOTP密钥是否为空
|
||||||
// 若为空 则生成一个 二级TOTP密钥 然后持久化写入到授权文件中
|
// 若为空 则生成一个 二级TOTP密钥 然后持久化写入到授权文件中
|
||||||
if service.authorizationInfo.SecondTOTPSecret == "" {
|
if service.authorizationInfo.SecondTOTPSecret == "" {
|
||||||
secondTOTPSecret, err := service.totpService.GenerateTierTwoTOTPSecret()
|
secondTOTPSecret, err := totp_tier_two.GenerateTierTwoTOTPSecret()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("生成二级TOTP密钥失败: %v", err)
|
wdd_log.Error("生成二级TOTP密钥失败: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
service.authorizationInfo.SecondTOTPSecret = secondTOTPSecret
|
service.authorizationInfo.SecondTOTPSecret = secondTOTPSecret
|
||||||
@@ -63,7 +69,7 @@ func NewAuthService() *AuthService {
|
|||||||
// 持久化写入到授权文件中
|
// 持久化写入到授权文件中
|
||||||
err = service.saveAuthorizationInfo()
|
err = service.saveAuthorizationInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("持久化写入授权文件失败: %v", err)
|
wdd_log.Error("持久化写入授权文件失败: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,8 +86,13 @@ func (as *AuthService) AddHostInfo(hostInfo models2.HostInfo) {
|
|||||||
as.mu.Lock()
|
as.mu.Lock()
|
||||||
defer as.mu.Unlock()
|
defer as.mu.Unlock()
|
||||||
|
|
||||||
hostKey := utils.GenerateHostKey(hostInfo)
|
hostKey, err := totp_tier_one.EncryptHostInfo(hostInfo, as.totpService.tierOneSecret)
|
||||||
as.hostInfoSet[hostKey] = hostInfo
|
if err != nil {
|
||||||
|
wdd_log.Error("加密主机信息失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
as.heartBeatHostInfoMap[hostKey] = hostInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateAuthorizationFile 生成授权文件
|
// GenerateAuthorizationFile 生成授权文件
|
||||||
@@ -90,96 +101,143 @@ func (as *AuthService) GenerateAuthorizationFile() (*models2.AuthorizationFile,
|
|||||||
defer as.mu.RUnlock()
|
defer as.mu.RUnlock()
|
||||||
|
|
||||||
// 检查是否有主机信息
|
// 检查是否有主机信息
|
||||||
if len(as.hostInfoSet) == 0 {
|
if len(as.heartBeatHostInfoMap) == 0 {
|
||||||
return nil, errors.New("没有可用的主机信息")
|
return nil, errors.New("没有可用的主机信息")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成TOTP验证码
|
|
||||||
totpCode, err := as.totpService.GenerateTierTwoTOTPCode(as.authorizationInfo.SecondTOTPSecret)
|
|
||||||
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)
|
encryptedHostMap := make(map[string]models2.HostInfo)
|
||||||
for _, hostInfo := range as.hostInfoSet {
|
for _, hostInfo := range as.heartBeatHostInfoMap {
|
||||||
encrypted, err := utils.EncryptHostInfo(hostInfo)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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{
|
authFile := &models2.AuthorizationFile{
|
||||||
EncryptedHosts: encryptedHosts,
|
EncryptedHostMap: encryptedHostMap,
|
||||||
TOTPCode: totpCode,
|
TOTPCode: tierOneTOTPCode,
|
||||||
CurrentTime: now,
|
CurrentTime: utils.CurentTimeString(),
|
||||||
FirstAuthTime: firstAuthTime,
|
ProjectNamespace: projectNamespace,
|
||||||
TimeOffset: timeOffset,
|
EncryptedNamespace: encryptedNamespace,
|
||||||
}
|
}
|
||||||
|
|
||||||
return authFile, nil
|
return authFile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessAuthorizationCode 处理授权码
|
// ProcessAuthorizationCode 处理授权码
|
||||||
func (as *AuthService) ProcessAuthorizationCode(code models2.AuthorizationCode) error {
|
func (as *AuthService) ProcessAuthorizationCode(authCode models2.AuthorizationCode) error {
|
||||||
as.mu.Lock()
|
as.mu.Lock()
|
||||||
defer as.mu.Unlock()
|
defer as.mu.Unlock()
|
||||||
|
|
||||||
// 验证TOTP
|
// 验证TOTP
|
||||||
if !as.totpService.VerifyTierTwoTOTPCode(code.TOTPCode, as.authorizationInfo.SecondTOTPSecret) {
|
if !as.totpService.VerifyTierOneTOTP(authCode.TOTPCode) {
|
||||||
|
wdd_log.Error("无效的授权码: TOTP验证失败")
|
||||||
return errors.New("无效的授权码: 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
|
firstAuthTime := now
|
||||||
if as.initialized {
|
if as.initialized {
|
||||||
|
if as.authorizationInfo.FirstAuthTime != "" {
|
||||||
|
wdd_log.Debug("已存在初次授权时间: %s", as.authorizationInfo.FirstAuthTime)
|
||||||
firstAuthTime = 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 {
|
if err != nil {
|
||||||
|
wdd_log.Error("加密授权码失败: %v", err)
|
||||||
return 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 {
|
if err != nil {
|
||||||
|
wdd_log.Error("加密授权码失败: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存授权信息
|
// 保存授权信息
|
||||||
as.authorizationInfo = models2.AuthorizationStorage{
|
as.authorizationInfo = &models2.AuthorizationStorage{
|
||||||
EncryptedCode: encryptedCode,
|
EncryptedAuthrizationCode: encryptedCode,
|
||||||
FirstAuthTime: firstAuthTime,
|
FirstAuthTime: firstAuthTime,
|
||||||
TimeOffset: timeOffset,
|
TimeOffset: timeOffset,
|
||||||
AuthorizedHosts: code.EncryptedHosts,
|
AuthorizedHostMap: serverInfoMap,
|
||||||
|
SecondTOTPSecret: as.authorizationInfo.SecondTOTPSecret,
|
||||||
|
ProjectNamespace: projectNamespace,
|
||||||
|
EncryptedNamespace: authCode.EncryptedNamespace,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存到文件
|
// 保存到文件
|
||||||
if err := as.saveAuthorizationInfo(); err != nil {
|
if err := as.saveAuthorizationInfo(); err != nil {
|
||||||
|
wdd_log.Error("保存授权信息失败: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置初始化状态
|
||||||
as.initialized = true
|
as.initialized = true
|
||||||
|
|
||||||
return nil
|
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 {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否在已授权列表中
|
// 检查是否在已授权列表中
|
||||||
for _, host := range as.authorizationInfo.AuthorizedHosts {
|
_, ok := as.authorizationInfo.AuthorizedHostMap[encrypted]
|
||||||
if host == encrypted {
|
return ok
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyAuthorizationTime 验证授权时间有效性
|
// VerifyAuthorizationTime 验证授权时间有效性
|
||||||
@@ -220,43 +273,87 @@ func (as *AuthService) VerifyAuthorizationTime() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前时间和存储的初次授权时间
|
// 获取当前时间和存储的初次授权时间
|
||||||
now := time.Now()
|
now := utils.CurentTimeString()
|
||||||
storedOffset := as.authorizationInfo.TimeOffset
|
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
|
offsetDiff := actualOffset - storedOffset
|
||||||
|
|
||||||
// 获取允许的时间偏移
|
// 获取允许的时间偏移
|
||||||
allowedOffset := config.GetConfig().Auth.TimeOffsetAllowed
|
allowedOffset := config.GetConfig().TierOneAuth.TimeOffsetAllowed
|
||||||
|
|
||||||
// 检查偏差是否超过允许范围
|
// 检查偏差是否超过允许范围
|
||||||
if offsetDiff > allowedOffset || offsetDiff < -allowedOffset {
|
if offsetDiff > allowedOffset || offsetDiff < -allowedOffset {
|
||||||
log.Printf("检测到时间篡改! 存储偏移: %d, 实际偏移: %d, 差值: %d",
|
wdd_log.Warn("检测到时间篡改! 存储偏移: %d, 实际偏移: %d, 差值: %d",
|
||||||
storedOffset, actualOffset, offsetDiff)
|
storedOffset, actualOffset, offsetDiff)
|
||||||
// 这里可以添加更多的处理逻辑,例如发送警告、禁用授权等
|
// 这里可以添加更多的处理逻辑,例如发送警告、禁用授权等
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAuthorizationInfo 获取授权信息
|
// GetAuthorizationInfo 获取授权信息
|
||||||
func (as *AuthService) GetAuthorizationInfo() interface{} {
|
func (as *AuthService) GetAuthorizationInfo() (models2.AuthorizationStorage, error) {
|
||||||
as.mu.RLock()
|
as.mu.RLock()
|
||||||
defer as.mu.RUnlock()
|
defer as.mu.RUnlock()
|
||||||
|
|
||||||
if !as.initialized {
|
if !as.initialized {
|
||||||
return map[string]interface{}{
|
return models2.AuthorizationStorage{}, errors.New("未授权")
|
||||||
"authorized": false,
|
|
||||||
"message": "未授权",
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return map[string]interface{}{
|
// 解密授权码
|
||||||
"authorized": true,
|
decryptedCode, err := totp_tier_one.Decrypt(as.authorizationInfo.EncryptedAuthrizationCode, as.totpService.tierOneSecret)
|
||||||
"authorized_hosts": len(as.authorizationInfo.AuthorizedHosts),
|
if err != nil {
|
||||||
"first_auth_time": as.authorizationInfo.FirstAuthTime,
|
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 保存授权信息到文件
|
// saveAuthorizationInfo 保存授权信息到文件
|
||||||
@@ -266,30 +363,40 @@ func (as *AuthService) saveAuthorizationInfo() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return os.WriteFile(config.GetConfig().Auth.AuthFilePath, data, 0600)
|
return os.WriteFile(config.GetConfig().TierOneAuth.LocalAuthFilePath, data, 0600)
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadAuthorizationInfo 从文件加载授权信息
|
// loadAuthorizationInfo 从文件加载授权信息
|
||||||
func (as *AuthService) loadAuthorizationInfo() {
|
func (as *AuthService) loadAuthorizationInfo() {
|
||||||
filePath := config.GetConfig().Auth.AuthFilePath
|
filePath := config.GetConfig().TierOneAuth.LocalAuthFilePath
|
||||||
|
|
||||||
// 检查文件是否存在
|
// 检查文件是否存在
|
||||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||||
|
// 初始化一个空的授权信息
|
||||||
|
as.authorizationInfo = &models2.AuthorizationStorage{}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := os.ReadFile(filePath)
|
data, err := os.ReadFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("读取授权文件失败: %v", err)
|
wdd_log.Error("读取授权文件失败: %v", err)
|
||||||
|
// 初始化一个空的授权信息
|
||||||
|
as.authorizationInfo = &models2.AuthorizationStorage{}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var authInfo models2.AuthorizationStorage
|
var authInfo *models2.AuthorizationStorage
|
||||||
if err := json.Unmarshal(data, &authInfo); err != nil {
|
if err := json.Unmarshal(data, &authInfo); err != nil {
|
||||||
log.Printf("解析授权文件失败: %v", err)
|
wdd_log.Error("解析授权文件失败: %v", err)
|
||||||
|
// 初始化一个空的授权信息
|
||||||
|
as.authorizationInfo = &models2.AuthorizationStorage{}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if authInfo == nil {
|
||||||
|
authInfo = &models2.AuthorizationStorage{}
|
||||||
|
}
|
||||||
|
|
||||||
as.authorizationInfo = authInfo
|
as.authorizationInfo = authInfo
|
||||||
as.initialized = true
|
as.initialized = true
|
||||||
}
|
}
|
||||||
|
|||||||
36
cmii-uav-watchdog/services/cmii_service.go
Normal file
36
cmii-uav-watchdog/services/cmii_service.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
@@ -2,8 +2,10 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"cmii-uav-watchdog-common/models"
|
"cmii-uav-watchdog-common/models"
|
||||||
|
"cmii-uav-watchdog-common/totp_tier_two"
|
||||||
|
"cmii-uav-watchdog-common/utils"
|
||||||
|
"cmii-uav-watchdog-common/wdd_log"
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,6 +32,7 @@ func (hs *HeartbeatService) ProcessHeartbeat(req models.HeartbeatRequest) (*mode
|
|||||||
|
|
||||||
secondTOTPSecret := hs.authService.authorizationInfo.SecondTOTPSecret
|
secondTOTPSecret := hs.authService.authorizationInfo.SecondTOTPSecret
|
||||||
if secondTOTPSecret == "" {
|
if secondTOTPSecret == "" {
|
||||||
|
wdd_log.Error("二级TOTP密钥为空")
|
||||||
return nil, errors.New("二级TOTP密钥为空")
|
return nil, errors.New("二级TOTP密钥为空")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,12 +50,13 @@ func (hs *HeartbeatService) ProcessHeartbeat(req models.HeartbeatRequest) (*mode
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查totp验证码是否有效
|
// 检查totp验证码是否有效
|
||||||
if !hs.totpService.VerifyTierTwoTOTPCode(req.TOTPCode, secondTOTPSecret) {
|
if !totp_tier_two.VerifyTierTwoTOTPCode(req.TOTPCode, secondTOTPSecret) {
|
||||||
// 解析认证主机的相关信息
|
// 解析认证主机的相关信息
|
||||||
|
|
||||||
// 计算 请求时间与当前时间的时间差
|
// 计算 请求时间与当前时间的时间差
|
||||||
|
utils.CurentTimeUnix()
|
||||||
diff := time.Now().Unix() - req.Timestamp
|
diff := time.Now().Unix() - req.Timestamp
|
||||||
log.Printf("心跳请求时间与当前时间的时间差: %d", diff)
|
wdd_log.Error("TOTP验证失败!心跳请求时间与当前时间的时间差: %d", diff)
|
||||||
|
|
||||||
return nil, errors.New("无效的TOTP验证码,请检查系统时间是否正确!")
|
return nil, errors.New("无效的TOTP验证码,请检查系统时间是否正确!")
|
||||||
}
|
}
|
||||||
@@ -61,7 +65,7 @@ func (hs *HeartbeatService) ProcessHeartbeat(req models.HeartbeatRequest) (*mode
|
|||||||
authorized := hs.authService.IsHostAuthorized(req.HostInfo)
|
authorized := hs.authService.IsHostAuthorized(req.HostInfo)
|
||||||
|
|
||||||
// 生成TOTP验证码
|
// 生成TOTP验证码
|
||||||
totpCode, err := hs.totpService.GenerateTierTwoTOTPCode(secondTOTPSecret)
|
totpCode, err := totp_tier_two.GenerateTierTwoTOTPCode(secondTOTPSecret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -82,3 +86,16 @@ func (hs *HeartbeatService) isTimestampValid(timestamp int64) bool {
|
|||||||
// 允许5分钟的时间偏差
|
// 允许5分钟的时间偏差
|
||||||
return diff < 300 && diff > -300
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,41 +1,31 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cmii-uav-watchdog-common/totp_tier_one"
|
||||||
"cmii-uav-watchdog/config"
|
"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服务
|
// TOTPService TOTP服务
|
||||||
type TOTPService struct {
|
type TOTPService struct {
|
||||||
secret string
|
tierOneSecret string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTOTPService 创建TOTP服务
|
// NewTOTPService 创建TOTP服务
|
||||||
func NewTOTPService() *TOTPService {
|
func NewTOTPService() *TOTPService {
|
||||||
|
secret := config.GetConfig().TierOneAuth.TierOneSecret
|
||||||
|
if secret == "" {
|
||||||
|
panic("TierOne TOTP tierOneSecret is not set ! can not start the service!")
|
||||||
|
}
|
||||||
|
|
||||||
return &TOTPService{
|
return &TOTPService{
|
||||||
secret: config.GetConfig().Auth.Secret,
|
tierOneSecret: secret,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateTierOneTOTP 生成一级TOTP验证码
|
// GenerateTierOneTOTP 生成一级TOTP验证码
|
||||||
func (ts *TOTPService) GenerateTierOneTOTP() (string, error) {
|
func (ts *TOTPService) GenerateTierOneTOTP() (string, error) {
|
||||||
// 使用当前时间生成TOTP
|
// 使用当前时间生成TOTP
|
||||||
code, err := totp.GenerateCode(ts.secret, time.Now())
|
code, err := totp_tier_one.GenerateTierOneTOTPCode(ts.tierOneSecret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -46,41 +36,7 @@ func (ts *TOTPService) GenerateTierOneTOTP() (string, error) {
|
|||||||
// VerifyTierOneTOTP 验证一级TOTP验证码
|
// VerifyTierOneTOTP 验证一级TOTP验证码
|
||||||
func (ts *TOTPService) VerifyTierOneTOTP(code string) bool {
|
func (ts *TOTPService) VerifyTierOneTOTP(code string) bool {
|
||||||
// 验证TOTP
|
// 验证TOTP
|
||||||
valid := totp.Validate(code, ts.secret)
|
valid := totp_tier_one.VerifyTierOneTOTPCode(code, ts.tierOneSecret)
|
||||||
if !valid {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
return valid
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user