cache.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. package middleware
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "regexp"
  7. "sort"
  8. "xg_dba/internal/models"
  9. "github.com/mitchellh/mapstructure"
  10. "github.com/spf13/viper"
  11. )
  12. // InitCache 初始化缓存,从指定目录中读取 TOML 文件内容并存储到 packageModels 映射中。
  13. func InitCache(folderPath string) {
  14. packageModels := make(map[string]models.Cache)
  15. err := filepath.Walk(folderPath, func(path string, info os.FileInfo, err error) error {
  16. if err != nil {
  17. return err
  18. }
  19. if info.IsDir() {
  20. listenFilePath := filepath.Join(path, "listen.toml")
  21. var connectInfo models.ConnectInfo
  22. var systemMetaInfo models.SystemMetaInfo
  23. // 如果 listen.toml 文件存在,解析它到 connectInfo 结构体中
  24. if fileExists(listenFilePath) {
  25. if err := parseTomlFile(listenFilePath, &connectInfo); err != nil {
  26. return err
  27. }
  28. }
  29. // 获取目录中最新的 os_info.toml 文件
  30. latestOsInfoFilePath, err := getLatestOsInfoFile(path)
  31. if err != nil {
  32. return err
  33. }
  34. // 如果找到最新的 os_info.toml 文件,解析它到 systemMetaInfo 结构体中
  35. if latestOsInfoFilePath != "" {
  36. if err := parseTomlFile(latestOsInfoFilePath, &systemMetaInfo); err != nil {
  37. return err
  38. }
  39. }
  40. // 使用主机地址作为键,将解析的信息存储到 packageModels 映射中
  41. host := connectInfo.Ssh.Host
  42. if host != "" {
  43. packageModels[host] = models.Cache{
  44. ConnectInfo: connectInfo,
  45. SystemMetaInfo: systemMetaInfo,
  46. }
  47. }
  48. }
  49. return nil
  50. })
  51. if err != nil {
  52. fmt.Printf("遍历路径 %q 时出错: %v\n", folderPath, err)
  53. return
  54. }
  55. // 打印每个主机的缓存数据
  56. for host, cache := range packageModels {
  57. fmt.Printf("主机: %s, 缓存: %+v\n", host, cache)
  58. }
  59. }
  60. // fileExists 检查给定路径的文件是否存在。
  61. func fileExists(filename string) bool {
  62. info, err := os.Stat(filename)
  63. if os.IsNotExist(err) {
  64. return false
  65. }
  66. return !info.IsDir()
  67. }
  68. // parseTomlFile 使用 viper 解析 TOML 文件到提供的结构体中。
  69. func parseTomlFile(filePath string, v interface{}) error {
  70. // 为了防止 viper 的全局状态干扰解析结果,使用新的 viper 实例
  71. vp := viper.New()
  72. vp.SetConfigFile(filePath)
  73. vp.SetConfigType("toml")
  74. if err := vp.ReadInConfig(); err != nil {
  75. return err
  76. }
  77. // 使用 `vp.AllSettings()` 将配置映射到提供的结构体
  78. settings := vp.AllSettings()
  79. if err := decodeSettings(settings, v); err != nil {
  80. return err
  81. }
  82. return nil
  83. }
  84. // decodeSettings 将 viper 的设置映射解码到目标结构体。
  85. func decodeSettings(settings map[string]interface{}, v interface{}) error {
  86. decoderConfig := &mapstructure.DecoderConfig{
  87. Result: v,
  88. TagName: "toml",
  89. WeaklyTypedInput: true,
  90. }
  91. decoder, err := mapstructure.NewDecoder(decoderConfig)
  92. if err != nil {
  93. return err
  94. }
  95. return decoder.Decode(settings)
  96. }
  97. // getLatestOsInfoFile 在给定目录中查找最新的 os_info TOML 文件。
  98. // 它查找匹配模式 "os_info_<timestamp>.toml" 的文件,并返回最新的一个。
  99. func getLatestOsInfoFile(dirPath string) (string, error) {
  100. pattern := regexp.MustCompile(`^os_info(?:_\d{8}_\d{6})?\.toml$`)
  101. files, err := os.ReadDir(dirPath)
  102. if err != nil {
  103. return "", err
  104. }
  105. var matchingFiles []string
  106. for _, file := range files {
  107. if !file.IsDir() && pattern.MatchString(file.Name()) {
  108. matchingFiles = append(matchingFiles, filepath.Join(dirPath, file.Name()))
  109. }
  110. }
  111. // 如果没有找到匹配的文件,返回空字符串
  112. if len(matchingFiles) == 0 {
  113. return "", nil
  114. }
  115. // 将匹配的文件按降序排序,以获取最新的文件
  116. sort.Slice(matchingFiles, func(i, j int) bool {
  117. return matchingFiles[i] > matchingFiles[j]
  118. })
  119. return matchingFiles[0], nil
  120. }