diff --git a/modules/git/repo_contributors.go b/modules/git/repo_contributors.go new file mode 100644 index 0000000..683157f --- /dev/null +++ b/modules/git/repo_contributors.go @@ -0,0 +1,76 @@ +package git + +import ( + "bufio" + "context" + "os" + "strconv" + "strings" + + gitea_git "code.gitea.io/gitea/modules/git" +) + +type RepoContributor struct { + Name string `json:"name"` + Email string `json:"email"` + Commits int `json:"commits"` +} + +func GetRepoContributors(repo *gitea_git.Repository, page, pageSize int) (int, []*RepoContributor, error) { + var total, skip int + var contributors []*RepoContributor + + skip = (page - 1) * pageSize + + stdoutReader, stdoutWriter, err := os.Pipe() + if err != nil { + return total, nil, err + } + defer func() { + _ = stdoutReader.Close() + _ = stdoutWriter.Close() + }() + cmd := gitea_git.NewCommand(repo.Ctx, "shortlog", "-sne", "HEAD") + + stderr := new(strings.Builder) + err = cmd.Run(&gitea_git.RunOpts{ + Env: []string{}, + Dir: repo.Path, + Stdout: stdoutWriter, + Stderr: stderr, + PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { + _ = stdoutWriter.Close() + scanner := bufio.NewScanner(stdoutReader) + scanner.Split(bufio.ScanLines) + var ca int + for scanner.Scan() { + total++ + if skip > 0 { + skip-- + } else { + if ca < pageSize { + l := strings.TrimSpace(scanner.Text()) + commits := l[0:strings.Index(l, "\t")] + commitsInt, err := strconv.Atoi(commits) + if err != nil { + return err + } + name := l[strings.Index(l, "\t")+1 : strings.Index(l, " <")] + email := l[strings.Index(l, "<")+1 : strings.Index(l, ">")] + contributors = append(contributors, &RepoContributor{ + Commits: commitsInt, + Name: name, + Email: email, + }) + ca++ + } + } + } + + _ = stdoutReader.Close() + return nil + }, + }) + + return total, contributors, nil +} diff --git a/modules/structs/hook.go b/modules/structs/hook.go index 4311606..df79c41 100644 --- a/modules/structs/hook.go +++ b/modules/structs/hook.go @@ -16,6 +16,13 @@ type CreateHookOption struct { Active bool `json:"active"` } +type EditHookOption struct { + Config map[string]string `json:"config"` + Events []string `json:"events"` + BranchFilter string `json:"branch_filter" binding:"GlobPattern"` + Active *bool `json:"active"` +} + type HookTask struct { ID int64 `json:"id"` UUID string `json:"uuid"` diff --git a/routers/hat/hat.go b/routers/hat/hat.go index 31cc2ae..b5f1096 100644 --- a/routers/hat/hat.go +++ b/routers/hat/hat.go @@ -128,6 +128,7 @@ func Routers(ctx gocontext.Context) *web.Route { m.Group("/hooks", func() { m.Combo("").Post(bind(hat_api.CreateHookOption{}), repo.CreateHook) m.Group("/{id}", func() { + m.Combo("").Patch(bind(hat_api.EditHookOption{}), repo.EditHook) m.Get("/hooktasks", repo.ListHookTask) }) }, reqToken(), reqAdmin(), reqWebhooksEnabled()) diff --git a/routers/hat/repo/hook.go b/routers/hat/repo/hook.go index 52264ec..f338cf7 100644 --- a/routers/hat/repo/hook.go +++ b/routers/hat/repo/hook.go @@ -16,8 +16,127 @@ import ( hat_webhook "code.gitlink.org.cn/Gitlink/gitea_hat.git/models/webhook" hat_convert "code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/convert" hat_api "code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/structs" + jsoniter "github.com/json-iterator/go" ) +func EditHook(ctx *context.APIContext) { + form := web.GetForm(ctx).(*hat_api.EditHookOption) + hookID := ctx.ParamsInt64(":id") + editRepoHook(ctx, form, hookID) +} + +func editRepoHook(ctx *context.APIContext, form *hat_api.EditHookOption, hookID int64) { + repo := ctx.Repo + hook, err := utils.GetRepoHook(ctx, repo.Repository.ID, hookID) + if err != nil { + return + } + + if !editHook(ctx, form, hook) { + return + } + + updated, err := utils.GetRepoHook(ctx, repo.Repository.ID, hookID) + if err != nil { + return + } + + ctx.JSON(http.StatusOK, convert.ToHook(repo.RepoLink, updated)) +} + +func editHook(ctx *context.APIContext, form *hat_api.EditHookOption, w *webhook.Webhook) bool { + if form.Config != nil { + if url, ok := form.Config["url"]; ok { + w.URL = url + } + if secret, ok := form.Config["secret"]; ok { + w.Secret = secret + } + + if ct, ok := form.Config["content_type"]; ok { + if !webhook.IsValidHookContentType(ct) { + ctx.Error(http.StatusUnprocessableEntity, "", "Invalid content type") + return false + } + w.ContentType = webhook.ToHookContentType(ct) + } + + if hw, ok := form.Config["http_method"]; ok { + if !isValidHookHttpMethod(hw) { + ctx.Error(http.StatusUnprocessableEntity, "", "Invalid http method") + return false + } + w.HTTPMethod = hw + } + + if w.Type == webhook.SLACK { + if channel, ok := form.Config["channel"]; ok { + json := jsoniter.ConfigCompatibleWithStandardLibrary + meta, err := json.Marshal(&webhook_service.SlackMeta{ + Channel: channel, + Username: form.Config["username"], + IconURL: form.Config["icon_url"], + Color: form.Config["color"], + }) + if err != nil { + ctx.Error(http.StatusInternalServerError, "slack: JSON marshal failed", err) + return false + } + w.Meta = string(meta) + } + } + } + + // Update events + if len(form.Events) == 0 { + form.Events = []string{"push"} + } + w.PushOnly = false + w.SendEverything = false + w.ChooseEvents = true + w.Create = util.IsStringInSlice(string(webhook.HookEventCreate), form.Events, true) + w.Push = util.IsStringInSlice(string(webhook.HookEventPush), form.Events, true) + w.PullRequest = util.IsStringInSlice(string(webhook.HookEventPullRequest), form.Events, true) + // w.Create = util.IsStringInSlice(string(webhook.HookEventCreate), form.Events, true) + w.Delete = util.IsStringInSlice(string(webhook.HookEventDelete), form.Events, true) + w.Fork = util.IsStringInSlice(string(webhook.HookEventFork), form.Events, true) + w.Issues = util.IsStringInSlice(string(webhook.HookEventIssues), form.Events, true) + w.Issues = issuesHook(form.Events, "issues_only") + w.IssueAssign = issuesHook(form.Events, string(webhook.HookEventIssueAssign)) + w.IssueLabel = issuesHook(form.Events, string(webhook.HookEventIssueLabel)) + w.IssueMilestone = issuesHook(form.Events, string(webhook.HookEventIssueMilestone)) + w.IssueComment = issuesHook(form.Events, string(webhook.HookEventIssueComment)) + // w.IssueComment = util.IsStringInSlice(string(webhook.HookEventIssueComment), form.Events, true) + w.Push = util.IsStringInSlice(string(webhook.HookEventPush), form.Events, true) + // w.PullRequest = util.IsStringInSlice(string(webhook.HookEventPullRequest), form.Events, true) + w.PullRequest = pullHook(form.Events, "pull_request_only") + w.PullRequestAssign = pullHook(form.Events, string(webhook.HookEventPullRequestAssign)) + w.PullRequestLabel = pullHook(form.Events, string(webhook.HookEventPullRequestLabel)) + w.PullRequestMilestone = pullHook(form.Events, string(webhook.HookEventPullRequestMilestone)) + w.PullRequestComment = pullHook(form.Events, string(webhook.HookEventPullRequestComment)) + w.PullRequestReview = pullHook(form.Events, "pull_request_review") + w.PullRequestSync = pullHook(form.Events, string(webhook.HookEventPullRequestSync)) + w.Repository = util.IsStringInSlice(string(webhook.HookEventRepository), form.Events, true) + w.Release = util.IsStringInSlice(string(webhook.HookEventRelease), form.Events, true) + w.BranchFilter = form.BranchFilter + w.HookEvent.BranchFilter = form.BranchFilter + + if err := w.UpdateEvent(); err != nil { + ctx.Error(http.StatusInternalServerError, "UpdateEvent", err) + return false + } + + if form.Active != nil { + w.IsActive = *form.Active + } + + if err := webhook.UpdateWebhook(w); err != nil { + ctx.Error(http.StatusInternalServerError, "UpdateWebhook", err) + return false + } + return true +} + func CreateHook(ctx *context.APIContext) { form := web.GetForm(ctx).(*hat_api.CreateHookOption) if !checkCreateHookOption(ctx, form) { diff --git a/routers/hat/repo/repo.go b/routers/hat/repo/repo.go index a92c2c0..8e206ba 100644 --- a/routers/hat/repo/repo.go +++ b/routers/hat/repo/repo.go @@ -483,11 +483,10 @@ func SetDiffViewStyle(ctx *context.Context) { func GetContributors(ctx *context.APIContext) { listOptions := utils.GetListOptions(ctx) - branch := ctx.FormString("branch") - total, list, err := hat_git.GetPaginateCodeAuthorsWithoutSince(ctx.Repo.GitRepo, branch, listOptions.Page, listOptions.PageSize) + total, list, err := hat_git.GetRepoContributors(ctx.Repo.GitRepo, listOptions.Page, listOptions.PageSize) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetContributors", err) + ctx.Error(http.StatusInternalServerError, "GetRepoContributors", err) return }