config.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. package config
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "github.com/pelletier/go-toml/v2"
  7. "go.uber.org/zap/zapcore"
  8. )
  9. // AppConfig 应用配置结构
  10. type AppConfig struct {
  11. Server ServerConfig `toml:"server"`
  12. Log LogConfig `toml:"log"`
  13. Audit AuditConfig `toml:"audit"`
  14. Storage StorageConfig `toml:"storage"`
  15. MCP MCPConfig `toml:"mcp"`
  16. AI AIConfig `toml:"ai"`
  17. }
  18. // ServerConfig 服务器配置
  19. type ServerConfig struct {
  20. Ip string `toml:"ip"`
  21. Port int `toml:"port"`
  22. }
  23. // LogConfig 日志配置
  24. type LogConfig struct {
  25. Level string `toml:"level"` // 日志级别: debug, info, warn, error, fatal
  26. Development bool `toml:"development"` // 开发模式
  27. Encoding string `toml:"encoding"` // 编码格式: json, console
  28. OutputPaths []string `toml:"output_paths"` // 输出路径
  29. LogDir string `toml:"log_dir"` // 日志目录
  30. }
  31. // AuditConfig 审计配置
  32. type AuditConfig struct {
  33. Enabled bool `toml:"enabled"` // 是否启用审计
  34. DatabasePath string `toml:"database_path"` // 数据库路径
  35. RetentionDays int `toml:"retention_days"` // 保留天数
  36. BufferSize int `toml:"buffer_size"` // 缓冲区大小
  37. }
  38. // StorageConfig 存储配置
  39. type StorageConfig struct {
  40. // 存储类型: "file" 或 "db"
  41. Type string `toml:"type"`
  42. // 文件存储配置
  43. ConfigPath string `toml:"config_path"` // 配置文件路径 (用于文件存储)
  44. SQLBaseDir string `toml:"sql_base_dir"` // 脚本(Script)基础目录 (用于文件存储)
  45. // 数据库存储配置
  46. DatabasePath string `toml:"database_path"` // 数据库文件路径 (用于数据库存储)
  47. // 通用配置
  48. DataFile string `toml:"data_file"` // 数据文件路径(兼容旧配置 key `data_file`,推荐使用 `config_path`)
  49. }
  50. // MCPConfig MCP 组件配置
  51. type MCPConfig struct {
  52. Enable bool `toml:"enable"`
  53. ServerName string `toml:"server_name"`
  54. ServerVersion string `toml:"server_version"`
  55. }
  56. // AIConfig AI 交互配置
  57. type AIConfig struct {
  58. Enable bool `toml:"enable"`
  59. }
  60. // LoadConfig 加载配置文件
  61. func LoadConfig(path string) (*AppConfig, error) {
  62. // 检查文件是否存在
  63. if _, err := os.Stat(path); os.IsNotExist(err) {
  64. // 如果配置文件不存在,创建默认配置
  65. if err := CreateDefaultConfig(path); err != nil {
  66. return nil, fmt.Errorf("创建默认配置文件失败: %w", err)
  67. }
  68. }
  69. // 使用 os.ReadFile (Go 1.16+)
  70. data, err := os.ReadFile(path)
  71. if err != nil {
  72. return nil, err
  73. }
  74. var cfg AppConfig
  75. if err := toml.Unmarshal(data, &cfg); err != nil {
  76. return nil, err
  77. }
  78. return &cfg, nil
  79. }
  80. // CreateDefaultConfig 创建默认配置文件
  81. func CreateDefaultConfig(configPath string) error {
  82. // 创建默认配置
  83. defaultConfig := &AppConfig{
  84. Server: ServerConfig{
  85. Ip: "127.0.0.1",
  86. Port: 8080,
  87. },
  88. Log: LogConfig{
  89. Level: "info",
  90. Development: false,
  91. Encoding: "json",
  92. OutputPaths: []string{"stdout", "app.log"},
  93. LogDir: "./DBconfig/logs",
  94. },
  95. Audit: AuditConfig{
  96. Enabled: true,
  97. DatabasePath: "./DBconfig/audit.db",
  98. RetentionDays: 90,
  99. BufferSize: 1000,
  100. },
  101. Storage: StorageConfig{
  102. Type: "db",
  103. ConfigPath: "./DBconfig/data.toml",
  104. SQLBaseDir: "./DBconfig/sqlfiles",
  105. DatabasePath: "./DBconfig/storage.db",
  106. DataFile: "./DBconfig/data.toml",
  107. },
  108. MCP: MCPConfig{
  109. Enable: false,
  110. ServerName: "dbview-mcp",
  111. ServerVersion: "0.1.0",
  112. },
  113. AI: AIConfig{
  114. Enable: false,
  115. },
  116. }
  117. // 确保配置目录存在
  118. configDir := filepath.Dir(configPath)
  119. if err := os.MkdirAll(configDir, 0755); err != nil {
  120. return fmt.Errorf("创建配置目录失败: %w", err)
  121. }
  122. // 创建必要的目录
  123. if err := defaultConfig.CreateDirectories(); err != nil {
  124. return fmt.Errorf("创建必要目录失败: %w", err)
  125. }
  126. // 将配置序列化为TOML格式
  127. data, err := toml.Marshal(defaultConfig)
  128. if err != nil {
  129. return fmt.Errorf("序列化配置失败: %w", err)
  130. }
  131. // 写入配置文件
  132. if err := os.WriteFile(configPath, data, 0644); err != nil {
  133. return fmt.Errorf("写入配置文件失败: %w", err)
  134. }
  135. return nil
  136. }
  137. // LoadConfigFromFile 从文件句柄加载配置
  138. func LoadConfigFromFile(file *os.File) (*AppConfig, error) {
  139. var cfg AppConfig
  140. if err := toml.NewDecoder(file).Decode(&cfg); err != nil {
  141. return nil, err
  142. }
  143. return &cfg, nil
  144. }
  145. // SetDefaults 设置默认值
  146. func (cfg *AppConfig) SetDefaults() {
  147. // 服务器默认值
  148. if cfg.Server.Port == 0 {
  149. cfg.Server.Port = 8080
  150. }
  151. // 日志默认值
  152. if cfg.Log.Level == "" {
  153. cfg.Log.Level = "info"
  154. }
  155. if cfg.Log.Encoding == "" {
  156. cfg.Log.Encoding = "json"
  157. }
  158. if len(cfg.Log.OutputPaths) == 0 {
  159. cfg.Log.OutputPaths = []string{"stdout"}
  160. }
  161. if cfg.Log.LogDir == "" {
  162. cfg.Log.LogDir = "./DBconfig/logs"
  163. }
  164. // 审计默认值
  165. if cfg.Audit.DatabasePath == "" {
  166. cfg.Audit.DatabasePath = "./DBconfig/audit.db"
  167. }
  168. if cfg.Audit.RetentionDays == 0 {
  169. cfg.Audit.RetentionDays = 90
  170. }
  171. if cfg.Audit.BufferSize == 0 {
  172. cfg.Audit.BufferSize = 1000
  173. }
  174. // 注意:Audit.Enabled 默认为 false,需要明确启用
  175. // 存储默认值
  176. if cfg.Storage.Type == "" {
  177. cfg.Storage.Type = "db" // 默认使用数据库存储
  178. }
  179. if cfg.Storage.ConfigPath == "" {
  180. cfg.Storage.ConfigPath = "./DBconfig/data.toml"
  181. }
  182. if cfg.Storage.SQLBaseDir == "" {
  183. cfg.Storage.SQLBaseDir = "./DBconfig/sqlfiles"
  184. }
  185. if cfg.Storage.DatabasePath == "" {
  186. cfg.Storage.DatabasePath = "./DBconfig/storage.db"
  187. }
  188. if cfg.Storage.DataFile == "" {
  189. cfg.Storage.DataFile = "./DBconfig/data.toml" // 兼容旧配置 key `data_file`(推荐使用 `config_path`)
  190. }
  191. // MCP 默认值
  192. if cfg.MCP.ServerName == "" {
  193. cfg.MCP.ServerName = "dbview-mcp"
  194. }
  195. if cfg.MCP.ServerVersion == "" {
  196. cfg.MCP.ServerVersion = "0.1.0"
  197. }
  198. // AI 默认值
  199. // 仅启用开关由 TOML 决定,其他 AI 配置改由存储加载
  200. }
  201. // GetLogLevel 将字符串日志级别转换为 zapcore.Level
  202. func (lc *LogConfig) GetLogLevel() zapcore.Level {
  203. switch lc.Level {
  204. case "debug":
  205. return zapcore.DebugLevel
  206. case "info":
  207. return zapcore.InfoLevel
  208. case "warn", "warning":
  209. return zapcore.WarnLevel
  210. case "error":
  211. return zapcore.ErrorLevel
  212. case "fatal":
  213. return zapcore.FatalLevel
  214. default:
  215. return zapcore.InfoLevel
  216. }
  217. }
  218. // GetOutputPaths 获取完整的输出路径列表
  219. func (lc *LogConfig) GetOutputPaths() []string {
  220. paths := make([]string, 0, len(lc.OutputPaths))
  221. for _, path := range lc.OutputPaths {
  222. if path == "stdout" {
  223. paths = append(paths, "stdout")
  224. } else if path == "stderr" {
  225. paths = append(paths, "stderr")
  226. } else {
  227. // 如果是相对路径,转换为绝对路径
  228. if !filepath.IsAbs(path) {
  229. absPath := filepath.Join(lc.LogDir, path)
  230. paths = append(paths, absPath)
  231. } else {
  232. paths = append(paths, path)
  233. }
  234. }
  235. }
  236. return paths
  237. }
  238. // Validate 验证配置的有效性
  239. func (cfg *AppConfig) Validate() error {
  240. // 验证服务器配置
  241. if cfg.Server.Port <= 0 || cfg.Server.Port > 65535 {
  242. return fmt.Errorf("invalid server port: %d", cfg.Server.Port)
  243. }
  244. // 验证日志配置
  245. validLevels := map[string]bool{
  246. "debug": true, "info": true, "warn": true, "warning": true,
  247. "error": true, "fatal": true,
  248. }
  249. if !validLevels[cfg.Log.Level] {
  250. return fmt.Errorf("invalid log level: %s", cfg.Log.Level)
  251. }
  252. validEncodings := map[string]bool{
  253. "json": true, "console": true,
  254. }
  255. if !validEncodings[cfg.Log.Encoding] {
  256. return fmt.Errorf("invalid log encoding: %s", cfg.Log.Encoding)
  257. }
  258. // 验证审计配置
  259. if cfg.Audit.RetentionDays < 0 {
  260. return fmt.Errorf("invalid audit retention days: %d", cfg.Audit.RetentionDays)
  261. }
  262. if cfg.Audit.BufferSize < 0 {
  263. return fmt.Errorf("invalid audit buffer size: %d", cfg.Audit.BufferSize)
  264. }
  265. // 验证存储配置
  266. validStorageTypes := map[string]bool{
  267. "file": true, "db": true,
  268. }
  269. if !validStorageTypes[cfg.Storage.Type] {
  270. return fmt.Errorf("invalid storage type: %s", cfg.Storage.Type)
  271. }
  272. // AI 配置:仅校验开关,具体配置由存储提供
  273. return nil
  274. }
  275. // CreateDirectories 创建必要的目录
  276. func (cfg *AppConfig) CreateDirectories() error {
  277. dirs := []string{
  278. cfg.Log.LogDir,
  279. filepath.Dir(cfg.Audit.DatabasePath),
  280. cfg.Storage.SQLBaseDir,
  281. filepath.Dir(cfg.Storage.DataFile),
  282. }
  283. // 根据存储类型添加额外的目录
  284. if cfg.Storage.Type == "db" {
  285. dirs = append(dirs, filepath.Dir(cfg.Storage.DatabasePath))
  286. }
  287. for _, dir := range dirs {
  288. if dir == "" {
  289. continue
  290. }
  291. if err := os.MkdirAll(dir, 0755); err != nil {
  292. return fmt.Errorf("failed to create directory %s: %w", dir, err)
  293. }
  294. }
  295. return nil
  296. }
  297. // GetStorageManager 根据配置创建存储管理器
  298. func (sc *StorageConfig) GetStorageManager() (interface{}, error) {
  299. switch sc.Type {
  300. case "file":
  301. // 这里需要导入storage包,但为了避免循环依赖,我们返回配置信息
  302. return map[string]string{
  303. "type": "file",
  304. "config_path": sc.ConfigPath,
  305. "sql_base_dir": sc.SQLBaseDir,
  306. }, nil
  307. case "db":
  308. return map[string]string{
  309. "type": "db",
  310. "database_path": sc.DatabasePath,
  311. }, nil
  312. default:
  313. return nil, fmt.Errorf("unsupported storage type: %s", sc.Type)
  314. }
  315. }
  316. // IsFileStorage 是否使用文件存储
  317. func (sc *StorageConfig) IsFileStorage() bool {
  318. return sc.Type == "file"
  319. }
  320. // IsDBStorage 是否使用数据库存储
  321. func (sc *StorageConfig) IsDBStorage() bool {
  322. return sc.Type == "db"
  323. }