package api import ( "encoding/json" "fmt" "log" "os" "git.x2erp.com/qdy/go-base/ctx" "git.x2erp.com/qdy/go-base/model/response" "git.x2erp.com/qdy/go-svc-code/internal/opencode/container" ) // StartInstanceHandler 启动项目OpenCode实例 // 路由: POST /api/opencode/projects/:id/start func StartInstanceHandler(manager *container.InstanceManager) func(string, map[string]interface{}, *ctx.RequestContext) (*response.QueryResult[interface{}], error) { return func(id string, req map[string]interface{}, reqCtx *ctx.RequestContext) (*response.QueryResult[interface{}], error) { log.Printf("启动项目实例请求: 项目ID=%s, 请求参数=%v, 请求参数类型: %T", id, req, req) log.Printf("RequestContext: %+v", reqCtx) // 获取租户ID tenantID, _ := GetRequestContext(reqCtx) if tenantID == "" { tenantID = "default" log.Printf("使用默认租户ID: %s", tenantID) } // 如果请求参数为空,尝试从opencode.json读取token token := "" toolURL := "" // 首先尝试从请求参数读取 if len(req) > 0 { toolURL, _ = req["tool_url"].(string) token, _ = req["token"].(string) } // 如果token为空,从opencode.json读取 if token == "" { token = readTokenFromOpenCodeConfig() if token != "" { log.Printf("从opencode.json读取token: %s...", token[:min(20, len(token))]) } } // 如果tool_url为空,使用默认值 if toolURL == "" { toolURL = "http://localhost:9060/api/dbtools/" log.Printf("使用默认tool_url: %s", toolURL) } // 验证参数 if toolURL == "" { return ErrorResponse(fmt.Errorf("tool_url参数必须提供且为非空字符串")) } if token == "" { log.Printf("警告: token为空,某些功能可能受限") // 允许token为空,某些工具可能不需要认证 } // 启动实例 instance, err := manager.StartInstance(id, tenantID, toolURL, token) if err != nil { log.Printf("启动实例失败: 项目=%s, 错误=%v", id, err) return ErrorResponse(fmt.Errorf("启动实例失败: %v", err)) } // 构建响应数据 instanceInfo := InstanceInfo{ ProjectID: instance.ProjectID, Port: instance.Port, PID: instance.PID, Status: string(instance.Status), ConfigPath: instance.ConfigPath, WorkDir: instance.WorkDir, APIBase: fmt.Sprintf("http://localhost:%d", instance.Port), ToolURL: instance.ToolURL, } log.Printf("实例启动成功: 项目=%s, 端口=%d, PID=%d", id, instance.Port, instance.PID) return SuccessResponseWithMessage(instanceInfo, "实例启动成功") } } // min 返回两个整数中的最小值 func min(a, b int) int { if a < b { return a } return b } // readTokenFromOpenCodeConfig 从.opencode/opencode.json读取token func readTokenFromOpenCodeConfig() string { configPath := "./.opencode/opencode.json" data, err := os.ReadFile(configPath) if err != nil { log.Printf("读取opencode.json失败: %v", err) return "" } var config map[string]interface{} if err := json.Unmarshal(data, &config); err != nil { log.Printf("解析opencode.json失败: %v", err) return "" } if token, ok := config["token"].(string); ok { return token } log.Printf("opencode.json中未找到token字段") return "" }