暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

generator.go 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. package config
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io/ioutil"
  6. "os"
  7. "path/filepath"
  8. "strings"
  9. )
  10. // ProjectConfig 项目配置参数
  11. type ProjectConfig struct {
  12. ProjectID string `json:"project_id"`
  13. TenantID string `json:"tenant_id"`
  14. ToolURL string `json:"tool_url"`
  15. Token string `json:"token"`
  16. Port int `json:"port"`
  17. BasePath string `json:"base_path"`
  18. }
  19. // ConfigGenerator 配置生成器
  20. type ConfigGenerator struct {
  21. globalTemplate map[string]interface{}
  22. }
  23. // NewConfigGenerator 创建配置生成器
  24. func NewConfigGenerator(globalConfigPath string) (*ConfigGenerator, error) {
  25. generator := &ConfigGenerator{}
  26. // 加载全局配置模板
  27. if err := generator.loadGlobalTemplate(globalConfigPath); err != nil {
  28. return nil, fmt.Errorf("加载全局配置模板失败: %w", err)
  29. }
  30. return generator, nil
  31. }
  32. // loadGlobalTemplate 加载全局配置模板
  33. func (g *ConfigGenerator) loadGlobalTemplate(configPath string) error {
  34. // 如果配置文件不存在,使用默认模板
  35. if _, err := os.Stat(configPath); os.IsNotExist(err) {
  36. g.globalTemplate = g.getDefaultTemplate()
  37. return nil
  38. }
  39. // 读取配置文件
  40. data, err := ioutil.ReadFile(configPath)
  41. if err != nil {
  42. return fmt.Errorf("读取配置文件失败: %w", err)
  43. }
  44. // 解析JSON
  45. var config map[string]interface{}
  46. if err := json.Unmarshal(data, &config); err != nil {
  47. return fmt.Errorf("解析配置文件失败: %w", err)
  48. }
  49. g.globalTemplate = config
  50. return nil
  51. }
  52. // getDefaultTemplate 获取默认配置模板
  53. func (g *ConfigGenerator) getDefaultTemplate() map[string]interface{} {
  54. return map[string]interface{}{
  55. "$schema": "https://opencode.ai/config.json",
  56. "mcp": map[string]interface{}{
  57. "my-remote-dbtools": map[string]interface{}{
  58. "type": "remote",
  59. "url": "${TOOL_URL}",
  60. "enabled": true,
  61. "headers": map[string]interface{}{
  62. "Authorization": "Bearer ${TOKEN}",
  63. "X-Project-ID": "${PROJECT_ID}",
  64. },
  65. },
  66. },
  67. "server": map[string]interface{}{
  68. "port": 0, // 动态填充
  69. "hostname": "localhost",
  70. },
  71. "tools": map[string]interface{}{},
  72. }
  73. }
  74. // Generate 生成项目专属配置
  75. func (g *ConfigGenerator) Generate(cfg ProjectConfig) (string, error) {
  76. // 深拷贝模板
  77. projectConfig := make(map[string]interface{})
  78. for k, v := range g.globalTemplate {
  79. projectConfig[k] = v
  80. }
  81. // 更新服务器端口
  82. if server, ok := projectConfig["server"].(map[string]interface{}); ok {
  83. server["port"] = cfg.Port
  84. }
  85. // 递归替换配置中的占位符
  86. g.replacePlaceholders(projectConfig, cfg)
  87. // 转换为JSON字符串
  88. data, err := json.MarshalIndent(projectConfig, "", " ")
  89. if err != nil {
  90. return "", fmt.Errorf("序列化配置失败: %w", err)
  91. }
  92. return string(data), nil
  93. }
  94. // replacePlaceholders 递归替换配置中的占位符
  95. func (g *ConfigGenerator) replacePlaceholders(config interface{}, cfg ProjectConfig) {
  96. switch v := config.(type) {
  97. case map[string]interface{}:
  98. for key, value := range v {
  99. switch val := value.(type) {
  100. case string:
  101. // 替换字符串中的占位符
  102. newVal := val
  103. if strings.Contains(newVal, "${PROJECT_ID}") {
  104. newVal = strings.ReplaceAll(newVal, "${PROJECT_ID}", cfg.ProjectID)
  105. }
  106. if strings.Contains(newVal, "${TENANT_ID}") {
  107. newVal = strings.ReplaceAll(newVal, "${TENANT_ID}", cfg.TenantID)
  108. }
  109. if strings.Contains(newVal, "${TOOL_URL}") {
  110. newVal = strings.ReplaceAll(newVal, "${TOOL_URL}", cfg.ToolURL)
  111. }
  112. if strings.Contains(newVal, "${TOKEN}") {
  113. tokenValue := cfg.Token
  114. if tokenValue == "" {
  115. tokenValue = "123" // 静态测试token
  116. }
  117. newVal = strings.ReplaceAll(newVal, "${TOKEN}", tokenValue)
  118. }
  119. if newVal != val {
  120. v[key] = newVal
  121. }
  122. case map[string]interface{}:
  123. g.replacePlaceholders(val, cfg)
  124. case []interface{}:
  125. for _, item := range val {
  126. if m, ok := item.(map[string]interface{}); ok {
  127. g.replacePlaceholders(m, cfg)
  128. }
  129. }
  130. }
  131. }
  132. }
  133. }
  134. // WriteToFile 将配置写入文件
  135. func (g *ConfigGenerator) WriteToFile(configStr, projectDir string) error {
  136. // 确保.opencode目录存在
  137. opencodeDir := filepath.Join(projectDir, ".opencode")
  138. if err := os.MkdirAll(opencodeDir, 0755); err != nil {
  139. return fmt.Errorf("创建.opencode目录失败: %w", err)
  140. }
  141. // 写入配置文件
  142. configPath := filepath.Join(opencodeDir, "opencode.json")
  143. if err := ioutil.WriteFile(configPath, []byte(configStr), 0644); err != nil {
  144. return fmt.Errorf("写入配置文件失败: %w", err)
  145. }
  146. return nil
  147. }
  148. // GenerateAndWrite 生成并写入配置文件
  149. func (g *ConfigGenerator) GenerateAndWrite(cfg ProjectConfig) (string, error) {
  150. // 生成配置
  151. configStr, err := g.Generate(cfg)
  152. if err != nil {
  153. return "", err
  154. }
  155. // 构建项目路径
  156. projectPath := filepath.Join(cfg.BasePath, cfg.ProjectID)
  157. // 写入文件
  158. if err := g.WriteToFile(configStr, projectPath); err != nil {
  159. return "", err
  160. }
  161. return configStr, nil
  162. }