diff --git a/internal/controllers/dependency_controller.go b/internal/controllers/dependency_controller.go index 1b4d085..991acc5 100644 --- a/internal/controllers/dependency_controller.go +++ b/internal/controllers/dependency_controller.go @@ -6,7 +6,6 @@ import ( "github.com/engigu/baihu-panel/internal/models" "github.com/engigu/baihu-panel/internal/models/vo" "github.com/engigu/baihu-panel/internal/services" - "github.com/engigu/baihu-panel/internal/services/deps" "github.com/engigu/baihu-panel/internal/utils" "github.com/gin-gonic/gin" @@ -304,96 +303,3 @@ func (c *DependencyController) GetInstalled(ctx *gin.Context) { utils.Success(ctx, packages) } - -// GetBatchInstallCommand 获取批量安装依赖包的命令 -func (c *DependencyController) GetBatchInstallCommand(ctx *gin.Context) { - var req struct { - Items []struct { - Name string `json:"name" binding:"required"` - Version string `json:"version"` - Language string `json:"language" binding:"required"` - LangVersion string `json:"lang_version"` - } `json:"items" binding:"required,gt=0"` - } - - if err := ctx.ShouldBindJSON(&req); err != nil { - utils.BadRequest(ctx, "参数错误: items 不能为空且必须包含 name 和 language") - return - } - - var depsList []models.Dependency - for _, item := range req.Items { - depsList = append(depsList, models.Dependency{ - Name: item.Name, - Version: item.Version, - Language: item.Language, - LangVersion: item.LangVersion, - }) - } - - cmd, err := c.service.GetBatchInstallCommand(depsList) - if err != nil { - utils.ServerError(ctx, err.Error()) - return - } - - utils.Success(ctx, gin.H{"command": cmd}) -} - -// ParseAndImport 解析上传/粘贴的清单文件内容并批量导入至数据库 -func (c *DependencyController) ParseAndImport(ctx *gin.Context) { - var req struct { - Language string `json:"language" binding:"required"` - LangVersion string `json:"lang_version"` - Content string `json:"content" binding:"required"` - ImportDB bool `json:"import_db"` // 是否持久化到数据库做可视化管理 - } - - if err := ctx.ShouldBindJSON(&req); err != nil { - utils.BadRequest(ctx, "参数错误: language 和 content 必填") - return - } - - // 1. 解析文本清单内容 - parsedDeps, err := deps.ParseManifest(req.Language, req.Content) - if err != nil { - utils.ServerError(ctx, "清单文件解析失败: "+err.Error()) - return - } - - if len(parsedDeps) == 0 { - utils.BadRequest(ctx, "未解析到任何有效依赖包") - return - } - - // 2. 补全语言和版本属性 - for i := range parsedDeps { - parsedDeps[i].Language = req.Language - parsedDeps[i].LangVersion = req.LangVersion - } - - // 3. 根据需求决定是否导入数据库 - var finalDeps []models.Dependency - if req.ImportDB { - imported, err := c.service.ImportDependencies(parsedDeps) - if err != nil { - utils.ServerError(ctx, "导入依赖记录至数据库失败: "+err.Error()) - return - } - finalDeps = imported - } else { - finalDeps = parsedDeps - } - - // 4. 为这一批包生成合并批量安装命令 - cmd, err := c.service.GetBatchInstallCommand(finalDeps) - if err != nil { - utils.ServerError(ctx, "生成安装命令失败: "+err.Error()) - return - } - - utils.Success(ctx, gin.H{ - "dependencies": vo.ToDependencyVOListFromModels(finalDeps), - "command": cmd, - }) -} diff --git a/internal/router/api_routes.go b/internal/router/api_routes.go index 6b716db..e0dd5cc 100644 --- a/internal/router/api_routes.go +++ b/internal/router/api_routes.go @@ -193,8 +193,6 @@ func registerDependencyRoutes(g *gin.RouterGroup, c *Controllers) { deps.POST("/reinstall/:id", c.Dependency.Reinstall) deps.POST("/reinstall-all", c.Dependency.ReinstallAll) deps.POST("/reinstall-all-cmd", c.Dependency.GetReinstallAllCommand) - deps.POST("/batch-install-cmd", c.Dependency.GetBatchInstallCommand) - deps.POST("/import", c.Dependency.ParseAndImport) deps.GET("/installed", c.Dependency.GetInstalled) } } diff --git a/internal/services/dependency_service.go b/internal/services/dependency_service.go index 58b0fb6..dc9ef77 100644 --- a/internal/services/dependency_service.go +++ b/internal/services/dependency_service.go @@ -111,30 +111,3 @@ func (s *DependencyService) GetVerifyCommand(language, langVersion string) (stri } return m.GetVerifyCommand(langVersion) } - -// GetBatchInstallCommand 获取批量安装命令 -func (s *DependencyService) GetBatchInstallCommand(depsList []models.Dependency) (string, error) { - if len(depsList) == 0 { - return "", errors.New("依赖包列表不能为空") - } - - firstDep := depsList[0] - m := deps.GetManager(firstDep.Language) - if m == nil { - return "", errors.New("不支持的依赖类型: " + firstDep.Language) - } - - return m.GetBatchInstallCommand(depsList) -} - -// ImportDependencies 批量导入依赖并自动入库去重 -func (s *DependencyService) ImportDependencies(depsList []models.Dependency) ([]models.Dependency, error) { - var imported []models.Dependency - for i := range depsList { - dep := &depsList[i] - if err := s.Create(dep); err == nil { - imported = append(imported, *dep) - } - } - return imported, nil -} diff --git a/internal/services/deps/manager.go b/internal/services/deps/manager.go index 0f44b8c..f8db8e2 100644 --- a/internal/services/deps/manager.go +++ b/internal/services/deps/manager.go @@ -16,7 +16,6 @@ type Manager interface { Uninstall(dep *models.Dependency) error GetInstalledPackages(language, langVersion string) ([]models.Dependency, error) GetInstallCommand(dep *models.Dependency) (string, error) - GetBatchInstallCommand(deps []models.Dependency) (string, error) GetReinstallAllCommand(deps []models.Dependency) (string, error) GetVerifyCommand(langVersion string) (string, error) } @@ -75,33 +74,6 @@ func (m *BaseManager) GetInstallCommand(dep *models.Dependency) (string, error) return fullCmd + " && echo \"__INSTALL_SUCCESS__\" || echo \"__INSTALL_FAILED__\"", nil } -func (m *BaseManager) GetBatchInstallCommand(deps []models.Dependency) (string, error) { - if len(deps) == 0 { - return "echo \"没有需要安装的依赖\"", nil - } - - var packageSpecs []string - var langVersion string - var language string - for _, dep := range deps { - language = dep.Language - if dep.LangVersion != "" { - langVersion = dep.LangVersion - } - if dep.Version != "" { - packageSpecs = append(packageSpecs, dep.Name+m.Separator+dep.Version) - } else { - packageSpecs = append(packageSpecs, dep.Name) - } - } - - args := append([]string{}, m.InstallCmd...) - args = append(args, packageSpecs...) - - fullCmd := utils.BuildMiseCommandSimple(strings.Join(args, " "), language, langVersion) - return fullCmd + " && echo \"__INSTALL_SUCCESS__\" || echo \"__INSTALL_FAILED__\"", nil -} - func (m *BaseManager) GetReinstallAllCommand(deps []models.Dependency) (string, error) { if len(deps) == 0 { return "echo \"没有需要安装的依赖\"", nil diff --git a/internal/services/deps/parser.go b/internal/services/deps/parser.go deleted file mode 100644 index 199468b..0000000 --- a/internal/services/deps/parser.go +++ /dev/null @@ -1,105 +0,0 @@ -package deps - -import ( - "encoding/json" - "regexp" - "strings" - - "github.com/engigu/baihu-panel/internal/models" -) - -// ParseManifest 根据语言解析依赖清单文件内容 -func ParseManifest(language, content string) ([]models.Dependency, error) { - lang := strings.ToLower(language) - if strings.Contains(lang, "python") { - return ParseRequirements(content), nil - } - if strings.Contains(lang, "node") { - return ParsePackageJson(content) - } - return []models.Dependency{}, nil -} - -// ParseRequirements 解析 Python requirements.txt -func ParseRequirements(content string) []models.Dependency { - var deps []models.Dependency - // 使用正则表达式按行分割,兼容 Windows 和 Linux 的换行符 - lines := regexp.MustCompile(`\r?\n`).Split(content, -1) - - // 用于分割包名和版本的正则 (支持 ==, >=, <=, ~=, >, <, @) - versionRegex := regexp.MustCompile(`[=><~@]+`) - - for _, line := range lines { - line = strings.TrimSpace(line) - // 忽略空行、注释行以及参数行 (以 - 开头的行如 -i, -r) - if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, "-") { - continue - } - - // 分割名称与版本 - parts := versionRegex.Split(line, 2) - name := strings.TrimSpace(parts[0]) - version := "" - if len(parts) > 1 { - // 清除可能存在的后续参数,比如 requests==2.31.0 --hash=sha256:... - versionPart := strings.TrimSpace(parts[1]) - - // 如果有逗号分隔的多个范围限制,比如 >=1.20,<2.0,只取第一个范围作为参考版本号 - if idx := strings.Index(versionPart, ","); idx != -1 { - versionPart = versionPart[:idx] - } - - versionFields := strings.Fields(versionPart) - if len(versionFields) > 0 { - version = strings.TrimSpace(versionFields[0]) - // 清除可能残留的首部版本符号 - version = strings.TrimLeft(version, "=><~@ ") - } - } - - if name != "" { - deps = append(deps, models.Dependency{ - Name: name, - Version: version, - Language: "python3", - }) - } - } - return deps -} - -// PackageJson 代表 package.json 的结构定义 -type PackageJson struct { - Dependencies map[string]string `json:"dependencies"` - DevDependencies map[string]string `json:"devDependencies"` -} - -// ParsePackageJson 解析 Node.js package.json -func ParsePackageJson(content string) ([]models.Dependency, error) { - var pkg PackageJson - if err := json.Unmarshal([]byte(content), &pkg); err != nil { - return nil, err - } - - var deps []models.Dependency - collect := func(m map[string]string, isDev bool) { - for name, versionRange := range m { - // 移除 npm 常见版本范围修饰符(如 ^1.2.3 或 ~2.3.0,保留底线版本号) - version := strings.TrimLeft(versionRange, "^~>=<* ") - remark := "" - if isDev { - remark = "devDependencies" - } - deps = append(deps, models.Dependency{ - Name: name, - Version: version, - Language: "node", - Remark: remark, - }) - } - } - - collect(pkg.Dependencies, false) - collect(pkg.DevDependencies, true) - return deps, nil -} diff --git a/internal/services/deps/parser_test.go b/internal/services/deps/parser_test.go deleted file mode 100644 index d584ebe..0000000 --- a/internal/services/deps/parser_test.go +++ /dev/null @@ -1,87 +0,0 @@ -package deps - -import ( - "testing" -) - -func TestParseRequirements(t *testing.T) { - content := ` -# This is a comment -requests==2.31.0 -numpy>=1.20,<2.0 -gunicorn --r other-requirements.txt - pandas ~= 1.3.0 -` - deps := ParseRequirements(content) - if len(deps) != 4 { - t.Fatalf("expected 4 dependencies, got %d", len(deps)) - } - - expected := []struct { - name string - version string - }{ - {"requests", "2.31.0"}, - {"numpy", "1.20"}, - {"gunicorn", ""}, - {"pandas", "1.3.0"}, - } - - for i, exp := range expected { - if deps[i].Name != exp.name { - t.Errorf("expected name %s, got %s", exp.name, deps[i].Name) - } - if deps[i].Version != exp.version { - t.Errorf("expected version %s, got %s", exp.version, deps[i].Version) - } - if deps[i].Language != "python3" { - t.Errorf("expected language python3, got %s", deps[i].Language) - } - } -} - -func TestParsePackageJson(t *testing.T) { - content := `{ - "dependencies": { - "lodash": "^4.17.21", - "express": "~4.18.2" - }, - "devDependencies": { - "typescript": "^5.0.4" - } -}` - deps, err := ParsePackageJson(content) - if err != nil { - t.Fatalf("failed to parse package.json: %v", err) - } - - if len(deps) != 3 { - t.Fatalf("expected 3 dependencies, got %d", len(deps)) - } - - expected := []struct { - name string - version string - remark string - }{ - {"lodash", "4.17.21", ""}, - {"express", "4.18.2", ""}, - {"typescript", "5.0.4", "devDependencies"}, - } - - for i, exp := range expected { - if deps[i].Name != exp.name { - t.Errorf("expected name %s, got %s", exp.name, deps[i].Name) - } - if deps[i].Version != exp.version { - t.Errorf("expected version %s, got %s", exp.version, deps[i].Version) - } - if deps[i].Remark != exp.remark { - t.Errorf("expected remark %s, got %s", exp.remark, deps[i].Remark) - } - if deps[i].Language != "node" { - t.Errorf("expected language node, got %s", deps[i].Language) - } - } -}