package config import ( "encoding/json" "fmt" "io/ioutil" "os" "path/filepath" "strings" ) // ProjectConfig 项目配置参数 type ProjectConfig struct { ProjectID string `json:"project_id"` TenantID string `json:"tenant_id"` ToolURL string `json:"tool_url"` Token string `json:"token"` Port int `json:"port"` BasePath string `json:"base_path"` } // ConfigGenerator 配置生成器 type ConfigGenerator struct { globalTemplate map[string]interface{} } // NewConfigGenerator 创建配置生成器 func NewConfigGenerator(globalConfigPath string) (*ConfigGenerator, error) { generator := &ConfigGenerator{} // 加载全局配置模板 if err := generator.loadGlobalTemplate(globalConfigPath); err != nil { return nil, fmt.Errorf("加载全局配置模板失败: %w", err) } return generator, nil } // loadGlobalTemplate 加载全局配置模板 func (g *ConfigGenerator) loadGlobalTemplate(configPath string) error { // 如果配置文件不存在,使用默认模板 if _, err := os.Stat(configPath); os.IsNotExist(err) { g.globalTemplate = g.getDefaultTemplate() return nil } // 读取配置文件 data, err := ioutil.ReadFile(configPath) if err != nil { return fmt.Errorf("读取配置文件失败: %w", err) } // 解析JSON var config map[string]interface{} if err := json.Unmarshal(data, &config); err != nil { return fmt.Errorf("解析配置文件失败: %w", err) } g.globalTemplate = config return nil } // getDefaultTemplate 获取默认配置模板 func (g *ConfigGenerator) getDefaultTemplate() map[string]interface{} { return map[string]interface{}{ "$schema": "https://opencode.ai/config.json", "mcp": map[string]interface{}{ "my-remote-dbtools": map[string]interface{}{ "type": "remote", "url": "${TOOL_URL}", "enabled": true, "headers": map[string]interface{}{ "Authorization": "Bearer ${TOKEN}", "X-Project-ID": "${PROJECT_ID}", }, }, }, "server": map[string]interface{}{ "port": 0, // 动态填充 "hostname": "localhost", }, "tools": map[string]interface{}{}, } } // Generate 生成项目专属配置 func (g *ConfigGenerator) Generate(cfg ProjectConfig) (string, error) { // 深拷贝模板 projectConfig := make(map[string]interface{}) for k, v := range g.globalTemplate { projectConfig[k] = v } // 更新服务器端口 if server, ok := projectConfig["server"].(map[string]interface{}); ok { server["port"] = cfg.Port } // 递归替换配置中的占位符 g.replacePlaceholders(projectConfig, cfg) // 转换为JSON字符串 data, err := json.MarshalIndent(projectConfig, "", " ") if err != nil { return "", fmt.Errorf("序列化配置失败: %w", err) } return string(data), nil } // replacePlaceholders 递归替换配置中的占位符 func (g *ConfigGenerator) replacePlaceholders(config interface{}, cfg ProjectConfig) { switch v := config.(type) { case map[string]interface{}: for key, value := range v { switch val := value.(type) { case string: // 替换字符串中的占位符 newVal := val if strings.Contains(newVal, "${PROJECT_ID}") { newVal = strings.ReplaceAll(newVal, "${PROJECT_ID}", cfg.ProjectID) } if strings.Contains(newVal, "${TENANT_ID}") { newVal = strings.ReplaceAll(newVal, "${TENANT_ID}", cfg.TenantID) } if strings.Contains(newVal, "${TOOL_URL}") { newVal = strings.ReplaceAll(newVal, "${TOOL_URL}", cfg.ToolURL) } if strings.Contains(newVal, "${TOKEN}") { tokenValue := cfg.Token if tokenValue == "" { tokenValue = "123" // 静态测试token } newVal = strings.ReplaceAll(newVal, "${TOKEN}", tokenValue) } if newVal != val { v[key] = newVal } case map[string]interface{}: g.replacePlaceholders(val, cfg) case []interface{}: for _, item := range val { if m, ok := item.(map[string]interface{}); ok { g.replacePlaceholders(m, cfg) } } } } } } // WriteToFile 将配置写入文件 func (g *ConfigGenerator) WriteToFile(configStr, projectDir string) error { // 确保.opencode目录存在 opencodeDir := filepath.Join(projectDir, ".opencode") if err := os.MkdirAll(opencodeDir, 0755); err != nil { return fmt.Errorf("创建.opencode目录失败: %w", err) } // 写入配置文件 configPath := filepath.Join(opencodeDir, "opencode.json") if err := ioutil.WriteFile(configPath, []byte(configStr), 0644); err != nil { return fmt.Errorf("写入配置文件失败: %w", err) } return nil } // GenerateAndWrite 生成并写入配置文件 func (g *ConfigGenerator) GenerateAndWrite(cfg ProjectConfig) (string, error) { // 生成配置 configStr, err := g.Generate(cfg) if err != nil { return "", err } // 构建项目路径 projectPath := filepath.Join(cfg.BasePath, cfg.ProjectID) // 写入文件 if err := g.WriteToFile(configStr, projectPath); err != nil { return "", err } return configStr, nil }