| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- 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
- }
|