package handlers import ( "crypto/sha1" "cs-bridge/internal/config" gitpkg "cs-bridge/pkg/git" "cs-bridge/pkg/logger" "encoding/hex" "encoding/json" "fmt" "net/http" "path" "strings" ) // GitProgressPage 显示git进度页面 func GitProgressPage() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // 获取repo参数 repo := r.URL.Query().Get("repo") if repo == "" { http.Error(w, "Missing repo parameter", http.StatusBadRequest) return } // 返回HTML页面 w.Header().Set("Content-Type", "text/html; charset=utf-8") logger.GetLogger().Info(fmt.Sprintf("[GitProgressPage] Repo参数: %s", repo)) // 使用字符串替换避免fmt格式化问题 html := strings.Replace(gitProgressHTML, "{{REPO_URL}}", repo, 1) w.Write([]byte(html)) } } // ExecuteGitRequest 执行git操作的请求结构 type ExecuteGitRequest struct { RepoURL string `json:"repo_url"` } // ExecuteGitResponse 执行git操作的响应结构 type ExecuteGitResponse struct { Success bool `json:"success"` Message string `json:"message"` RedirectURL string `json:"redirect_url,omitempty"` Error string `json:"error,omitempty"` } // ExecuteGitOperations 执行git操作(克隆或更新) func ExecuteGitOperations(cfg *config.Config) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } // 解析请求 var req ExecuteGitRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { SendProgressJSON(w, "error", "Invalid request", 0) return } if req.RepoURL == "" { SendProgressJSON(w, "error", "Missing repo_url", 0) return } log := logger.GetLogger() log.Info(fmt.Sprintf("[ExecuteGit] 开始执行Git操作, RepoURL: %s, ClientIP: %s", req.RepoURL, r.RemoteAddr)) // 生成workspace路径 - 使用客户端IP和仓库URL的SHA1哈希作为workspaceID repoName := gitpkg.GetRepoName(req.RepoURL) // 基于客户端IP和仓库URL生成稳定的workspaceID // hashInput := r.RemoteAddr + ":" + req.RepoURL hashInput := req.RepoURL hasher := sha1.New() hasher.Write([]byte(hashInput)) workspaceID := hex.EncodeToString(hasher.Sum(nil))[:16] // 取前16位 log.Info(fmt.Sprintf("[ExecuteGit] 生成workspaceID: %s (基于: %s)", workspaceID, hashInput)) // 容器内路径(传递给code-server) - 使用path.Join确保Linux风格路径 containerWorkspacePath := path.Join(cfg.CodeServer.WorkspaceRoot, workspaceID, repoName) // SSH服务器上的实际路径(用于git操作) - 使用path.Join确保Linux风格路径 sshWorkspacePath := containerWorkspacePath if cfg.CodeServer.SSHWorkspaceRoot != "" { sshWorkspacePath = path.Join(cfg.CodeServer.SSHWorkspaceRoot, workspaceID, repoName) } log.Info(fmt.Sprintf("[ExecuteGit] 路径配置 - Container: %s, SSH: %s", containerWorkspacePath, sshWorkspacePath)) // 进度回调函数 progressCallback := func(message string, percent int) { SendProgressToClients("processing", message, percent) } // 检查是否使用SSH远程执行 useSSH := cfg.CodeServer.SSHHost != "" var sshCfg gitpkg.SSHConfig if useSSH { sshCfg = gitpkg.SSHConfig{ Host: cfg.CodeServer.SSHHost, Port: cfg.CodeServer.SSHPort, User: cfg.CodeServer.SSHUser, KeyPath: cfg.CodeServer.SSHKeyPath, } } // 检查仓库是否存在 SendProgressToClients("checking", "正在检查仓库...", 5) var err error var repoExists bool if useSSH { repoExists = gitpkg.CheckRepoExistsRemote(sshCfg, sshWorkspacePath) } else { repoExists = gitpkg.CheckRepoExists(containerWorkspacePath) } if repoExists { // 仓库存在,执行pull SendProgressToClients("pulling", "仓库已存在,正在更新...", 20) if useSSH { err = gitpkg.PullRepoRemote(sshCfg, sshWorkspacePath, progressCallback) } else { err = gitpkg.PullRepo(containerWorkspacePath, progressCallback) } } else { // 仓库不存在,执行clone SendProgressToClients("cloning", "仓库不存在,正在克隆...", 20) if useSSH { err = gitpkg.CloneRepoRemote(sshCfg, req.RepoURL, sshWorkspacePath, progressCallback) } else { err = gitpkg.CloneRepo(req.RepoURL, containerWorkspacePath, progressCallback) } } if err != nil { SendProgressToClients("error", fmt.Sprintf("Git操作失败: %v", err), 0) json.NewEncoder(w).Encode(ExecuteGitResponse{ Success: false, Error: err.Error(), }) return } // Git操作成功,发送100%进度 SendProgressToClients("success", "操作完成,即将跳转...", 100) // 直接重定向到code-server,带folder参数 redirectURL := fmt.Sprintf("%s?folder=%s", cfg.CodeServer.BaseURL, containerWorkspacePath) log.Info(fmt.Sprintf("[ExecuteGit] 准备重定向到: %s", redirectURL)) // 返回成功响应 w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(ExecuteGitResponse{ Success: true, Message: "Git操作完成", RedirectURL: redirectURL, }) } } // gitProgressHTML 是git进度页面的HTML模板 const gitProgressHTML = `