package controller import ( "errors" "net/http" "net/url" "strings" "github.com/heicode/manager/common" "github.com/heicode/manager/model" "github.com/gin-gonic/gin" "gorm.io/gorm" ) type gitSourcePayload struct { Name string `json:"name"` Provider string `json:"provider"` RepoURL string `json:"repo_url"` Ref string `json:"ref"` Paths []string `json:"paths"` Usage string `json:"usage"` TenantId string `json:"tenant_id"` Status string `json:"status"` } type gitSourceResponse struct { Id int `json:"id"` UserId int `json:"user_id"` TenantId string `json:"tenant_id"` Name string `json:"name"` Provider string `json:"provider"` RepoURL string `json:"repo_url"` Ref string `json:"ref"` Paths []string `json:"paths"` Usage string `json:"usage"` Status string `json:"status"` CreatedAt int64 `json:"created_at"` UpdatedAt int64 `json:"updated_at"` } func normalizeGitSourcePayload(p gitSourcePayload) (gitSourcePayload, error) { p.Name = strings.TrimSpace(p.Name) p.Provider = strings.TrimSpace(p.Provider) p.RepoURL = strings.TrimSpace(p.RepoURL) p.Ref = strings.TrimSpace(p.Ref) p.Usage = strings.TrimSpace(p.Usage) p.TenantId = strings.TrimSpace(p.TenantId) p.Status = strings.TrimSpace(p.Status) if p.Name == "" { return p, errors.New("name required") } if p.RepoURL == "" { return p, errors.New("repo_url required") } u, err := url.Parse(p.RepoURL) if err != nil || u.Scheme == "" || u.Host == "" { return p, errors.New("repo_url must be a valid http(s) URL") } if p.Ref == "" { p.Ref = "main" } if p.Provider == "" { p.Provider = "custom" } if p.Usage == "" { p.Usage = "project" } if p.Status == "" { p.Status = "active" } paths := make([]string, 0, len(p.Paths)) for _, path := range p.Paths { path = strings.TrimSpace(path) if path == "" { continue } if strings.HasPrefix(path, "/") || strings.Contains(path, "..") { return p, errors.New("paths must be relative and must not contain ..") } paths = append(paths, path) } if len(paths) == 0 { paths = []string{"."} } p.Paths = paths return p, nil } func gitSourceToResponse(src model.GitSource) gitSourceResponse { var paths []string if src.Paths != "" { _ = common.UnmarshalJsonStr(src.Paths, &paths) } if len(paths) == 0 { paths = []string{"."} } return gitSourceResponse{ Id: src.Id, UserId: src.UserId, TenantId: src.TenantId, Name: src.Name, Provider: src.Provider, RepoURL: src.RepoURL, Ref: src.Ref, Paths: paths, Usage: src.Usage, Status: src.Status, CreatedAt: src.CreatedAt, UpdatedAt: src.UpdatedAt, } } func ListGitSources(c *gin.Context) { userId := c.GetInt("id") var sources []model.GitSource if err := model.DB.Where("user_id = ?", userId).Order("id desc").Find(&sources).Error; err != nil { common.ApiError(c, err) return } items := make([]gitSourceResponse, 0, len(sources)) for _, src := range sources { items = append(items, gitSourceToResponse(src)) } common.ApiSuccess(c, gin.H{"items": items}) } func CreateGitSource(c *gin.Context) { userId := c.GetInt("id") var payload gitSourcePayload if err := c.ShouldBindJSON(&payload); err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid params"}) return } payload, err := normalizeGitSourcePayload(payload) if err != nil { common.ApiError(c, err) return } paths, err := common.Marshal(payload.Paths) if err != nil { common.ApiError(c, err) return } src := model.GitSource{ UserId: userId, TenantId: payload.TenantId, Name: payload.Name, Provider: payload.Provider, RepoURL: payload.RepoURL, Ref: payload.Ref, Paths: string(paths), Usage: payload.Usage, Status: payload.Status, } if err := model.DB.Create(&src).Error; err != nil { common.ApiError(c, err) return } common.ApiSuccess(c, gitSourceToResponse(src)) } func UpdateGitSource(c *gin.Context) { userId := c.GetInt("id") var src model.GitSource if err := model.DB.Where("id = ? AND user_id = ?", c.Param("id"), userId).First(&src).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { common.ApiErrorMsg(c, "git source not found") return } common.ApiError(c, err) return } var payload gitSourcePayload if err := c.ShouldBindJSON(&payload); err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid params"}) return } payload, err := normalizeGitSourcePayload(payload) if err != nil { common.ApiError(c, err) return } paths, err := common.Marshal(payload.Paths) if err != nil { common.ApiError(c, err) return } src.TenantId = payload.TenantId src.Name = payload.Name src.Provider = payload.Provider src.RepoURL = payload.RepoURL src.Ref = payload.Ref src.Paths = string(paths) src.Usage = payload.Usage src.Status = payload.Status if err := model.DB.Save(&src).Error; err != nil { common.ApiError(c, err) return } common.ApiSuccess(c, gitSourceToResponse(src)) } func DeleteGitSource(c *gin.Context) { userId := c.GetInt("id") res := model.DB.Where("id = ? AND user_id = ?", c.Param("id"), userId).Delete(&model.GitSource{}) if res.Error != nil { common.ApiError(c, res.Error) return } if res.RowsAffected == 0 { common.ApiErrorMsg(c, "git source not found") return } common.ApiSuccess(c, gin.H{"deleted": true}) }