diff --git a/models/bots/runner.go b/models/bots/runner.go index 37f42e9d3f..6bbabd88cb 100644 --- a/models/bots/runner.go +++ b/models/bots/runner.go @@ -65,7 +65,7 @@ func (Runner) TableName() string { func (r *Runner) OwnType() string { if r.OwnerID == 0 { - return "Global Type" + return "Global" } if r.RepoID == 0 { return r.Owner.Name @@ -91,6 +91,42 @@ func (r *Runner) AllLabels() []string { return append(r.AgentLabels, r.CustomLabels...) } +// Editable checks if the runner is editable by the user +func (r *Runner) Editable(ownerID, repoID int64) bool { + if ownerID == 0 && repoID == 0 { + return true + } + if ownerID > 0 && r.OwnerID == ownerID { + return true + } + return repoID > 0 && r.RepoID == repoID +} + +// LoadAttributes loads the attributes of the runner +func (r *Runner) LoadAttributes(ctx context.Context) error { + if r.OwnerID > 0 { + var user user_model.User + has, err := db.GetEngine(ctx).ID(r.OwnerID).Get(&user) + if err != nil { + return err + } + if has { + r.Owner = &user + } + } + if r.RepoID > 0 { + var repo repo_model.Repository + has, err := db.GetEngine(ctx).ID(r.RepoID).Get(&repo) + if err != nil { + return err + } + if has { + r.Repo = &repo + } + } + return nil +} + func init() { db.RegisterModel(&Runner{}) } @@ -106,13 +142,20 @@ type FindRunnerOptions struct { func (opts FindRunnerOptions) toCond() builder.Cond { cond := builder.NewCond() + + withGlobal := false if opts.RepoID > 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) + withGlobal = true } if opts.OwnerID > 0 { cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) + withGlobal = true } - cond = cond.Or(builder.Eq{"repo_id": 0, "owner_id": 0}) + if withGlobal { + cond = cond.Or(builder.Eq{"repo_id": 0, "owner_id": 0}) + } + if opts.Filter != "" { cond = cond.And(builder.Like{"name", opts.Filter}) } @@ -223,6 +266,7 @@ func UpdateRunner(ctx context.Context, r *Runner, cols ...string) error { return err } +// DeleteRunner deletes a runner by given ID. func DeleteRunner(ctx context.Context, r *Runner) error { e := db.GetEngine(ctx) _, err := e.Delete(r) diff --git a/routers/common/runners.go b/routers/common/runners.go new file mode 100644 index 0000000000..703fb46408 --- /dev/null +++ b/routers/common/runners.go @@ -0,0 +1,118 @@ +package common + +import ( + "net/http" + "strings" + + bots_model "code.gitea.io/gitea/models/bots" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/forms" +) + +// RunnersList render common runners list page +func RunnersList(ctx *context.Context, tplName base.TplName, opts bots_model.FindRunnerOptions) { + + count, err := bots_model.CountRunners(opts) + if err != nil { + ctx.ServerError("AdminRunners", err) + return + } + + runners, err := bots_model.FindRunners(opts) + if err != nil { + ctx.ServerError("AdminRunners", err) + return + } + if err := runners.LoadAttributes(ctx); err != nil { + ctx.ServerError("LoadAttributes", err) + return + } + + // ownid=0,repo_id=0,means this token is used for global + var token *bots_model.RunnerToken + token, err = bots_model.GetUnactivatedRunnerToken(opts.OwnerID, opts.RepoID) + if _, ok := err.(bots_model.ErrRunnerTokenNotExist); ok { + token, err = bots_model.NewRunnerToken(opts.OwnerID, opts.RepoID) + if err != nil { + ctx.ServerError("CreateRunnerToken", err) + return + } + } else { + if err != nil { + ctx.ServerError("GetUnactivatedRunnerToken", err) + return + } + } + + ctx.Data["Keyword"] = opts.Filter + ctx.Data["Runners"] = runners + ctx.Data["Total"] = count + ctx.Data["RegistrationToken"] = token.Token + ctx.Data["RunnerOnwerID"] = opts.OwnerID + ctx.Data["RunnerRepoID"] = opts.RepoID + + pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5) + ctx.Data["Page"] = pager + + ctx.HTML(http.StatusOK, tplName) +} + +func RunnerDetails(ctx *context.Context, tplName base.TplName, runnerID int64) { + runner, err := bots_model.GetRunnerByID(runnerID) + if err != nil { + ctx.ServerError("GetRunnerByID", err) + return + } + if err := runner.LoadAttributes(ctx); err != nil { + ctx.ServerError("LoadAttributes", err) + return + } + + ctx.Data["Runner"] = runner + + // TODO: get task list for this runner + + ctx.HTML(http.StatusOK, tplName) +} + +// RunnerDetailsEditPost response for edit runner details +func RunnerDetailsEditPost(ctx *context.Context, runnerID int64, redirectTo string) { + runner, err := bots_model.GetRunnerByID(runnerID) + if err != nil { + log.Warn("RunnerDetailsEditPost.GetRunnerByID failed: %v, url: %s", err, ctx.Req.URL) + ctx.ServerError("RunnerDetailsEditPost.GetRunnerByID", err) + return + } + + form := web.GetForm(ctx).(*forms.EditRunnerForm) + runner.Description = form.Description + runner.CustomLabels = strings.Split(form.CustomLabels, ",") + + err = bots_model.UpdateRunner(ctx, runner, "description", "custom_labels") + if err != nil { + log.Warn("RunnerDetailsEditPost.UpdateRunner failed: %v, url: %s", err, ctx.Req.URL) + ctx.Flash.Warning(ctx.Tr("admin.runners.update_runner_failed")) + ctx.Redirect(redirectTo) + return + } + + log.Debug("RunnerDetailsEditPost success: %s", ctx.Req.URL) + + ctx.Flash.Success(ctx.Tr("admin.runners.update_runner_success")) + ctx.Redirect(redirectTo) +} + +// RunnerResetRegistrationToken reset registration token +func RunnerResetRegistrationToken(ctx *context.Context, ownerID, repoID int64, redirectTo string) { + _, err := bots_model.NewRunnerToken(ownerID, repoID) + if err != nil { + ctx.ServerError("ResetRunnerRegistrationToken", err) + return + } + + ctx.Flash.Success(ctx.Tr("admin.runners.reset_registration_token_success")) + ctx.Redirect(redirectTo) +} diff --git a/routers/web/admin/runners.go b/routers/web/admin/runners.go index 3d1db1a9be..bdfc52f2e6 100644 --- a/routers/web/admin/runners.go +++ b/routers/web/admin/runners.go @@ -6,9 +6,7 @@ package admin import ( - "net/http" "net/url" - "strings" bots_model "code.gitea.io/gitea/models/bots" "code.gitea.io/gitea/models/db" @@ -16,14 +14,13 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/web" - "code.gitea.io/gitea/services/forms" + "code.gitea.io/gitea/routers/common" ) const ( - tplRunners base.TplName = "runners/list" - tplRunnerNew base.TplName = "runners/new" - tplRunnerEdit base.TplName = "runners/edit" + tplRunners base.TplName = "admin/runners/base" + tplRunnerNew base.TplName = "admin/runners/new" + tplRunnerEdit base.TplName = "admin/runners/edit" ) // Runners show all the runners @@ -45,49 +42,11 @@ func Runners(ctx *context.Context) { Sort: ctx.Req.URL.Query().Get("sort"), Filter: ctx.Req.URL.Query().Get("q"), WithDeleted: false, + RepoID: 0, + OwnerID: 0, } - count, err := bots_model.CountRunners(opts) - if err != nil { - ctx.ServerError("AdminRunners", err) - return - } - - runners, err := bots_model.FindRunners(opts) - if err != nil { - ctx.ServerError("AdminRunners", err) - return - } - if err := runners.LoadAttributes(ctx); err != nil { - ctx.ServerError("LoadAttributes", err) - return - } - - // ownid=0,repo_id=0,means this token is used for global - var token *bots_model.RunnerToken - token, err = bots_model.GetUnactivatedRunnerToken(0, 0) - if _, ok := err.(bots_model.ErrRunnerTokenNotExist); ok { - token, err = bots_model.NewRunnerToken(0, 0) - if err != nil { - ctx.ServerError("CreateRunnerToken", err) - return - } - } else { - if err != nil { - ctx.ServerError("GetUnactivatedRunnerToken", err) - return - } - } - - ctx.Data["Keyword"] = opts.Filter - ctx.Data["Runners"] = runners - ctx.Data["Total"] = count - ctx.Data["RegistrationToken"] = token.Token - - pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5) - ctx.Data["Page"] = pager - - ctx.HTML(http.StatusOK, tplRunners) + common.RunnersList(ctx, tplRunners, opts) } // EditRunner show editing runner page @@ -96,47 +55,16 @@ func EditRunner(ctx *context.Context) { ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdminRunners"] = true - runner, err := bots_model.GetRunnerByID(ctx.ParamsInt64(":runnerid")) - if err != nil { - ctx.ServerError("GetRunnerByID", err) - return - } - ctx.Data["Runner"] = runner - - // TODO: get task list for this runner - - ctx.HTML(http.StatusOK, tplRunnerEdit) + common.RunnerDetails(ctx, tplRunnerEdit, ctx.ParamsInt64(":runnerid")) } // EditRunnerPost response for editing runner func EditRunnerPost(ctx *context.Context) { - runner, err := bots_model.GetRunnerByID(ctx.ParamsInt64(":runnerid")) - if err != nil { - log.Warn("EditRunnerPost.GetRunnerByID failed: %v, url: %s", err, ctx.Req.URL) - ctx.ServerError("EditRunnerPost.GetRunnerByID", err) - return - } - - form := web.GetForm(ctx).(*forms.AdminEditRunnerForm) - runner.Description = form.Description - runner.CustomLabels = strings.Split(form.CustomLabels, ",") - - err = bots_model.UpdateRunner(ctx, runner, "description", "custom_labels") - if err != nil { - log.Warn("EditRunnerPost.UpdateRunner failed: %v, url: %s", err, ctx.Req.URL) - ctx.Flash.Warning(ctx.Tr("admin.runners.update_runner_failed")) - ctx.Redirect(setting.AppSubURL + "/admin/runners/" + url.PathEscape(ctx.Params(":runnerid"))) - return - } - ctx.Data["Title"] = ctx.Tr("admin.runners.edit") ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdminRunners"] = true - - log.Debug("EditRunnerPost success: %s", ctx.Req.URL) - - ctx.Flash.Success(ctx.Tr("admin.runners.update_runner_success")) - ctx.Redirect(setting.AppSubURL + "/admin/runners/" + url.PathEscape(ctx.Params(":runnerid"))) + common.RunnerDetailsEditPost(ctx, ctx.ParamsInt64(":runnerid"), + setting.AppSubURL+"/admin/runners/"+url.PathEscape(ctx.Params(":runnerid"))) } // DeleteRunner response for deleting a runner @@ -163,14 +91,7 @@ func DeleteRunnerPost(ctx *context.Context) { } func ResetRunnerRegistrationToken(ctx *context.Context) { - _, err := bots_model.NewRunnerToken(0, 0) - if err != nil { - ctx.ServerError("ResetRunnerRegistrationToken", err) - return - } - - ctx.Flash.Success(ctx.Tr("admin.runners.reset_registration_token_success")) - ctx.Redirect(setting.AppSubURL + "/admin/runners/") + common.RunnerResetRegistrationToken(ctx, 0, 0, setting.AppSubURL+"/admin/runners/") } /** diff --git a/routers/web/org/org_runners.go b/routers/web/org/org_runners.go index d7b6d47a17..661b4090be 100644 --- a/routers/web/org/org_runners.go +++ b/routers/web/org/org_runners.go @@ -1,26 +1,41 @@ package org import ( - "net/http" - - "code.gitea.io/gitea/models/webhook" + bots_model "code.gitea.io/gitea/models/bots" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/routers/common" ) // Runners render runners page func Runners(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("org.settings") + ctx.Data["Title"] = ctx.Tr("org.runners") ctx.Data["PageIsOrgSettings"] = true ctx.Data["PageIsOrgSettingsRunners"] = true - ctx.Data["BaseLink"] = ctx.Org.OrgLink + "/settings/runners" - ctx.Data["Description"] = ctx.Tr("org.settings.runners_desc") - ws, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{OrgID: ctx.Org.Organization.ID}) - if err != nil { - ctx.ServerError("GetWebhooksByOrgId", err) - return + page := ctx.FormInt("page") + if page <= 1 { + page = 1 } - ctx.Data["Webhooks"] = ws - ctx.HTML(http.StatusOK, tplSettingsRunners) + opts := bots_model.FindRunnerOptions{ + ListOptions: db.ListOptions{ + Page: page, + PageSize: 100, + }, + Sort: ctx.Req.URL.Query().Get("sort"), + Filter: ctx.Req.URL.Query().Get("q"), + WithDeleted: false, + RepoID: 0, + OwnerID: ctx.Org.Organization.ID, + } + + common.RunnersList(ctx, tplSettingsRunners, opts) +} + +// ResetRunnerRegistrationToken reset runner registration token +func ResetRunnerRegistrationToken(ctx *context.Context) { + common.RunnerResetRegistrationToken(ctx, + ctx.Org.Organization.ID, 0, + ctx.Org.OrgLink+"/settings/runners") } diff --git a/routers/web/web.go b/routers/web/web.go index 88c9cc6c9d..9d91a1285c 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -628,7 +628,7 @@ func RegisterRoutes(m *web.Route) { m.Group("/runners", func() { m.Get("", admin.Runners) m.Get("/reset_registration_token", admin.ResetRunnerRegistrationToken) - m.Combo("/{runnerid}").Get(admin.EditRunner).Post(bindIgnErr(forms.AdminEditRunnerForm{}), admin.EditRunnerPost) + m.Combo("/{runnerid}").Get(admin.EditRunner).Post(bindIgnErr(forms.EditRunnerForm{}), admin.EditRunnerPost) m.Post("/{runnerid}/delete", admin.DeleteRunnerPost) }) }, func(ctx *context.Context) { @@ -799,6 +799,7 @@ func RegisterRoutes(m *web.Route) { m.Group("/runners", func() { m.Get("", org.Runners) + m.Get("/reset_registration_token", org.ResetRunnerRegistrationToken) }) m.Route("/delete", "GET,POST", org.SettingsDelete) diff --git a/services/forms/admin.go b/services/forms/admin.go index 8a1e679f68..537b9f982c 100644 --- a/services/forms/admin.go +++ b/services/forms/admin.go @@ -71,27 +71,3 @@ func (f *AdminDashboardForm) Validate(req *http.Request, errs binding.Errors) bi ctx := context.GetContext(req) return middleware.Validate(errs, ctx.Data, f, ctx.Locale) } - -// AdminCreateRunnerForm form for admin to create runner -type AdminCreateRunnerForm struct { - Name string `binding:"Required"` - Type string -} - -// Validate validates form fields -func (f *AdminCreateRunnerForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { - ctx := context.GetContext(req) - return middleware.Validate(errs, ctx.Data, f, ctx.Locale) -} - -// AdminEditRunnerForm form for admin to create runner -type AdminEditRunnerForm struct { - Description string - CustomLabels string -} - -// Validate validates form fields -func (f *AdminEditRunnerForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { - ctx := context.GetContext(req) - return middleware.Validate(errs, ctx.Data, f, ctx.Locale) -} diff --git a/services/forms/runner.go b/services/forms/runner.go new file mode 100644 index 0000000000..e1f633bb8b --- /dev/null +++ b/services/forms/runner.go @@ -0,0 +1,33 @@ +package forms + +import ( + "net/http" + + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/web/middleware" + "gitea.com/go-chi/binding" +) + +// EditRunnerForm form for admin to create runner +type EditRunnerForm struct { + Description string + CustomLabels string +} + +// Validate validates form fields +func (f *EditRunnerForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middleware.Validate(errs, ctx.Data, f, ctx.Locale) +} + +// CreateRunnerForm form for admin to create runner +type CreateRunnerForm struct { + Name string `binding:"Required"` + Type string +} + +// Validate validates form fields +func (f *CreateRunnerForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetContext(req) + return middleware.Validate(errs, ctx.Data, f, ctx.Locale) +} diff --git a/templates/admin/runners/base.tmpl b/templates/admin/runners/base.tmpl new file mode 100644 index 0000000000..dbf8c86070 --- /dev/null +++ b/templates/admin/runners/base.tmpl @@ -0,0 +1,8 @@ +{{template "base/head" .}} +