From 6433ba0ec3dfde67f45267aa12bd713c4a44c740 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 26 Jan 2021 23:36:53 +0800 Subject: Move macaron to chi (#14293) Use [chi](https://github.com/go-chi/chi) instead of the forked [macaron](https://gitea.com/macaron/macaron). Since macaron and chi have conflicts with session share, this big PR becomes a have-to thing. According my previous idea, we can replace macaron step by step but I'm wrong. :( Below is a list of big changes on this PR. - [x] Define `context.ResponseWriter` interface with an implementation `context.Response`. - [x] Use chi instead of macaron, and also a customize `Route` to wrap chi so that the router usage is similar as before. - [x] Create different routers for `web`, `api`, `internal` and `install` so that the codes will be more clear and no magic . - [x] Use https://github.com/unrolled/render instead of macaron's internal render - [x] Use https://github.com/NYTimes/gziphandler instead of https://gitea.com/macaron/gzip - [x] Use https://gitea.com/go-chi/session which is a modified version of https://gitea.com/macaron/session and removed `nodb` support since it will not be maintained. **BREAK** - [x] Use https://gitea.com/go-chi/captcha which is a modified version of https://gitea.com/macaron/captcha - [x] Use https://gitea.com/go-chi/cache which is a modified version of https://gitea.com/macaron/cache - [x] Use https://gitea.com/go-chi/binding which is a modified version of https://gitea.com/macaron/binding - [x] Use https://github.com/go-chi/cors instead of https://gitea.com/macaron/cors - [x] Dropped https://gitea.com/macaron/i18n and make a new one in `code.gitea.io/gitea/modules/translation` - [x] Move validation form structs from `code.gitea.io/gitea/modules/auth` to `code.gitea.io/gitea/modules/forms` to avoid dependency cycle. - [x] Removed macaron log service because it's not need any more. **BREAK** - [x] All form structs have to be get by `web.GetForm(ctx)` in the route function but not as a function parameter on routes definition. - [x] Move Git HTTP protocol implementation to use routers directly. - [x] Fix the problem that chi routes don't support trailing slash but macaron did. - [x] `/api/v1/swagger` now will be redirect to `/api/swagger` but not render directly so that `APIContext` will not create a html render. Notices: - Chi router don't support request with trailing slash - Integration test `TestUserHeatmap` maybe mysql version related. It's failed on my macOS(mysql 5.7.29 installed via brew) but succeed on CI. Co-authored-by: 6543 <6543@obermui.de> --- routers/admin/admin.go | 11 +- routers/admin/auths.go | 9 +- routers/admin/users.go | 9 +- routers/admin/users_test.go | 12 +- routers/api/v1/admin/org.go | 5 +- routers/api/v1/admin/repo.go | 7 +- routers/api/v1/admin/user.go | 15 +- routers/api/v1/api.go | 245 ++++--- routers/api/v1/misc/markdown.go | 8 +- routers/api/v1/misc/markdown_test.go | 34 +- routers/api/v1/org/hook.go | 14 +- routers/api/v1/org/label.go | 9 +- routers/api/v1/org/org.go | 9 +- routers/api/v1/org/team.go | 9 +- routers/api/v1/repo/branch.go | 11 +- routers/api/v1/repo/collaborators.go | 5 +- routers/api/v1/repo/commits.go | 6 +- routers/api/v1/repo/file.go | 11 +- routers/api/v1/repo/fork.go | 4 +- routers/api/v1/repo/git_hook.go | 4 +- routers/api/v1/repo/hook.go | 14 +- routers/api/v1/repo/issue.go | 12 +- routers/api/v1/repo/issue_comment.go | 15 +- routers/api/v1/repo/issue_label.go | 12 +- routers/api/v1/repo/issue_reaction.go | 25 +- routers/api/v1/repo/issue_tracked_time.go | 5 +- routers/api/v1/repo/key.go | 4 +- routers/api/v1/repo/label.go | 7 +- routers/api/v1/repo/migrate.go | 7 +- routers/api/v1/repo/milestone.go | 8 +- routers/api/v1/repo/pull.go | 14 +- routers/api/v1/repo/pull_review.go | 18 +- routers/api/v1/repo/release.go | 8 +- routers/api/v1/repo/release_attachment.go | 7 +- routers/api/v1/repo/repo.go | 21 +- routers/api/v1/repo/repo_test.go | 9 +- routers/api/v1/repo/status.go | 4 +- routers/api/v1/repo/topic.go | 4 +- routers/api/v1/repo/transfer.go | 5 +- routers/api/v1/swagger/options.go | 2 +- routers/api/v1/user/app.go | 14 +- routers/api/v1/user/email.go | 9 +- routers/api/v1/user/gpg_key.go | 6 +- routers/api/v1/user/key.go | 6 +- routers/init.go | 10 +- routers/install.go | 54 +- routers/org/org.go | 6 +- routers/org/org_labels.go | 12 +- routers/org/setting.go | 9 +- routers/org/teams.go | 9 +- routers/private/hook.go | 12 +- routers/private/internal.go | 82 ++- routers/private/key.go | 7 +- routers/private/mail.go | 8 +- routers/private/manager.go | 20 +- routers/private/manager_unix.go | 7 +- routers/private/manager_windows.go | 7 +- routers/private/serv.go | 7 +- routers/repo/branch.go | 6 +- routers/repo/editor.go | 25 +- routers/repo/http.go | 178 +++-- routers/repo/issue.go | 17 +- routers/repo/issue_label.go | 12 +- routers/repo/issue_label_test.go | 12 +- routers/repo/issue_lock.go | 7 +- routers/repo/issue_timetrack.go | 6 +- routers/repo/migrate.go | 10 +- routers/repo/milestone.go | 9 +- routers/repo/projects.go | 17 +- routers/repo/pull.go | 17 +- routers/repo/pull_review.go | 9 +- routers/repo/release.go | 9 +- routers/repo/release_test.go | 6 +- routers/repo/repo.go | 6 +- routers/repo/setting.go | 14 +- routers/repo/setting_protected_branch.go | 6 +- routers/repo/settings_test.go | 9 +- routers/repo/webhook.go | 59 +- routers/repo/wiki.go | 9 +- routers/repo/wiki_test.go | 12 +- routers/routes/base.go | 246 +++++++ routers/routes/chi.go | 309 --------- routers/routes/install.go | 116 ++++ routers/routes/macaron.go | 993 --------------------------- routers/routes/recovery.go | 109 --- routers/routes/web.go | 1044 +++++++++++++++++++++++++++++ routers/swagger_json.go | 8 +- routers/user/auth.go | 44 +- routers/user/auth_openid.go | 21 +- routers/user/oauth.go | 16 +- routers/user/setting/account.go | 13 +- routers/user/setting/account_test.go | 6 +- routers/user/setting/applications.go | 6 +- routers/user/setting/keys.go | 6 +- routers/user/setting/oauth2.go | 9 +- routers/user/setting/profile.go | 11 +- routers/user/setting/security_openid.go | 10 +- routers/user/setting/security_twofa.go | 6 +- routers/user/setting/security_u2f.go | 14 +- 99 files changed, 2335 insertions(+), 2030 deletions(-) create mode 100644 routers/routes/base.go delete mode 100644 routers/routes/chi.go create mode 100644 routers/routes/install.go delete mode 100644 routers/routes/macaron.go delete mode 100644 routers/routes/recovery.go create mode 100644 routers/routes/web.go (limited to 'routers') diff --git a/routers/admin/admin.go b/routers/admin/admin.go index 4180076b08..250bc5da5e 100644 --- a/routers/admin/admin.go +++ b/routers/admin/admin.go @@ -16,20 +16,20 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/cron" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/queue" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/mailer" - "gitea.com/macaron/macaron" - "gitea.com/macaron/session" + "gitea.com/go-chi/session" ) const ( @@ -132,7 +132,8 @@ func Dashboard(ctx *context.Context) { } // DashboardPost run an admin operation -func DashboardPost(ctx *context.Context, form auth.AdminDashboardForm) { +func DashboardPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AdminDashboardForm) ctx.Data["Title"] = ctx.Tr("admin.dashboard") ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdminDashboard"] = true @@ -239,7 +240,7 @@ func Config(ctx *context.Context) { ctx.Data["OfflineMode"] = setting.OfflineMode ctx.Data["DisableRouterLog"] = setting.DisableRouterLog ctx.Data["RunUser"] = setting.RunUser - ctx.Data["RunMode"] = strings.Title(macaron.Env) + ctx.Data["RunMode"] = strings.Title(setting.RunMode) if version, err := git.LocalVersion(); err == nil { ctx.Data["GitVersion"] = version.Original() } diff --git a/routers/admin/auths.go b/routers/admin/auths.go index 7a9d286373..12d0a2ccfa 100644 --- a/routers/admin/auths.go +++ b/routers/admin/auths.go @@ -10,15 +10,16 @@ import ( "regexp" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/auth/ldap" "code.gitea.io/gitea/modules/auth/oauth2" "code.gitea.io/gitea/modules/auth/pam" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "xorm.io/xorm/convert" ) @@ -206,7 +207,8 @@ func parseSSPIConfig(ctx *context.Context, form auth.AuthenticationForm) (*model } // NewAuthSourcePost response for adding an auth source -func NewAuthSourcePost(ctx *context.Context, form auth.AuthenticationForm) { +func NewAuthSourcePost(ctx *context.Context) { + form := *web.GetForm(ctx).(*auth.AuthenticationForm) ctx.Data["Title"] = ctx.Tr("admin.auths.new") ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdminAuthentications"] = true @@ -312,7 +314,8 @@ func EditAuthSource(ctx *context.Context) { } // EditAuthSourcePost response for editing auth source -func EditAuthSourcePost(ctx *context.Context, form auth.AuthenticationForm) { +func EditAuthSourcePost(ctx *context.Context) { + form := *web.GetForm(ctx).(*auth.AuthenticationForm) ctx.Data["Title"] = ctx.Tr("admin.auths.edit") ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdminAuthentications"] = true diff --git a/routers/admin/users.go b/routers/admin/users.go index 74fce9a10c..2d40a883af 100644 --- a/routers/admin/users.go +++ b/routers/admin/users.go @@ -11,12 +11,13 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/password" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers" router_user_setting "code.gitea.io/gitea/routers/user/setting" "code.gitea.io/gitea/services/mailer" @@ -63,7 +64,8 @@ func NewUser(ctx *context.Context) { } // NewUserPost response for adding a new user -func NewUserPost(ctx *context.Context, form auth.AdminCreateUserForm) { +func NewUserPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AdminCreateUserForm) ctx.Data["Title"] = ctx.Tr("admin.users.new_account") ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdminUsers"] = true @@ -214,7 +216,8 @@ func EditUser(ctx *context.Context) { } // EditUserPost response for editting user -func EditUserPost(ctx *context.Context, form auth.AdminEditUserForm) { +func EditUserPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AdminEditUserForm) ctx.Data["Title"] = ctx.Tr("admin.users.edit_account") ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdminUsers"] = true diff --git a/routers/admin/users_test.go b/routers/admin/users_test.go index a282507f56..bd00bb2bf1 100644 --- a/routers/admin/users_test.go +++ b/routers/admin/users_test.go @@ -8,8 +8,9 @@ import ( "testing" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/modules/web" "github.com/stretchr/testify/assert" ) @@ -39,7 +40,8 @@ func TestNewUserPost_MustChangePassword(t *testing.T) { MustChangePassword: true, } - NewUserPost(ctx, form) + web.SetForm(ctx, &form) + NewUserPost(ctx) assert.NotEmpty(t, ctx.Flash.SuccessMsg) @@ -76,7 +78,8 @@ func TestNewUserPost_MustChangePasswordFalse(t *testing.T) { MustChangePassword: false, } - NewUserPost(ctx, form) + web.SetForm(ctx, &form) + NewUserPost(ctx) assert.NotEmpty(t, ctx.Flash.SuccessMsg) @@ -113,7 +116,8 @@ func TestNewUserPost_InvalidEmail(t *testing.T) { MustChangePassword: false, } - NewUserPost(ctx, form) + web.SetForm(ctx, &form) + NewUserPost(ctx) assert.NotEmpty(t, ctx.Flash.ErrorMsg) } diff --git a/routers/api/v1/admin/org.go b/routers/api/v1/admin/org.go index 0fd9e17f41..1356276f07 100644 --- a/routers/api/v1/admin/org.go +++ b/routers/api/v1/admin/org.go @@ -13,12 +13,13 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/utils" ) // CreateOrg api for create organization -func CreateOrg(ctx *context.APIContext, form api.CreateOrgOption) { +func CreateOrg(ctx *context.APIContext) { // swagger:operation POST /admin/users/{username}/orgs admin adminCreateOrg // --- // summary: Create an organization @@ -43,7 +44,7 @@ func CreateOrg(ctx *context.APIContext, form api.CreateOrgOption) { // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.CreateOrgOption) u := user.GetUserByParams(ctx) if ctx.Written() { return diff --git a/routers/api/v1/admin/repo.go b/routers/api/v1/admin/repo.go index 73c7d740f8..467f8a22ff 100644 --- a/routers/api/v1/admin/repo.go +++ b/routers/api/v1/admin/repo.go @@ -7,12 +7,13 @@ package admin import ( "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/repo" "code.gitea.io/gitea/routers/api/v1/user" ) // CreateRepo api for creating a repository -func CreateRepo(ctx *context.APIContext, form api.CreateRepoOption) { +func CreateRepo(ctx *context.APIContext) { // swagger:operation POST /admin/users/{username}/repos admin adminCreateRepo // --- // summary: Create a repository on behalf of a user @@ -41,11 +42,11 @@ func CreateRepo(ctx *context.APIContext, form api.CreateRepoOption) { // "$ref": "#/responses/error" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.CreateRepoOption) owner := user.GetUserByParams(ctx) if ctx.Written() { return } - repo.CreateUserRepo(ctx, owner, form) + repo.CreateUserRepo(ctx, owner, *form) } diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 670baf020b..f148710c79 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/password" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/mailer" @@ -42,7 +43,7 @@ func parseLoginSource(ctx *context.APIContext, u *models.User, sourceID int64, l } // CreateUser create a user -func CreateUser(ctx *context.APIContext, form api.CreateUserOption) { +func CreateUser(ctx *context.APIContext) { // swagger:operation POST /admin/users admin adminCreateUser // --- // summary: Create a user @@ -64,7 +65,7 @@ func CreateUser(ctx *context.APIContext, form api.CreateUserOption) { // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.CreateUserOption) u := &models.User{ Name: form.Username, FullName: form.FullName, @@ -119,7 +120,7 @@ func CreateUser(ctx *context.APIContext, form api.CreateUserOption) { } // EditUser api for modifying a user's information -func EditUser(ctx *context.APIContext, form api.EditUserOption) { +func EditUser(ctx *context.APIContext) { // swagger:operation PATCH /admin/users/{username} admin adminEditUser // --- // summary: Edit an existing user @@ -144,7 +145,7 @@ func EditUser(ctx *context.APIContext, form api.EditUserOption) { // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.EditUserOption) u := user.GetUserByParams(ctx) if ctx.Written() { return @@ -283,7 +284,7 @@ func DeleteUser(ctx *context.APIContext) { } // CreatePublicKey api for creating a public key to a user -func CreatePublicKey(ctx *context.APIContext, form api.CreateKeyOption) { +func CreatePublicKey(ctx *context.APIContext) { // swagger:operation POST /admin/users/{username}/keys admin adminCreatePublicKey // --- // summary: Add a public key on behalf of a user @@ -308,12 +309,12 @@ func CreatePublicKey(ctx *context.APIContext, form api.CreateKeyOption) { // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.CreateKeyOption) u := user.GetUserByParams(ctx) if ctx.Written() { return } - user.CreateUserPublicKey(ctx, form, u.ID) + user.CreateUserPublicKey(ctx, *form, u.ID) } // DeleteUserPublicKey api for deleting a user's public key diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 876f48ca5c..593551d770 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -66,14 +66,16 @@ package v1 import ( "net/http" + "reflect" "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/admin" "code.gitea.io/gitea/routers/api/v1/misc" "code.gitea.io/gitea/routers/api/v1/notify" @@ -83,11 +85,12 @@ import ( _ "code.gitea.io/gitea/routers/api/v1/swagger" // for swagger generation "code.gitea.io/gitea/routers/api/v1/user" - "gitea.com/macaron/binding" - "gitea.com/macaron/macaron" + "gitea.com/go-chi/binding" + "gitea.com/go-chi/session" + "github.com/go-chi/cors" ) -func sudo() macaron.Handler { +func sudo() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { sudo := ctx.Query("sudo") if len(sudo) == 0 { @@ -117,10 +120,10 @@ func sudo() macaron.Handler { } } -func repoAssignment() macaron.Handler { +func repoAssignment() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { - userName := ctx.Params(":username") - repoName := ctx.Params(":reponame") + userName := ctx.Params("username") + repoName := ctx.Params("reponame") var ( owner *models.User @@ -184,7 +187,7 @@ func repoAssignment() macaron.Handler { } // Contexter middleware already checks token for user sign in process. -func reqToken() macaron.Handler { +func reqToken() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if true == ctx.Data["IsApiToken"] { return @@ -201,7 +204,7 @@ func reqToken() macaron.Handler { } } -func reqBasicAuth() macaron.Handler { +func reqBasicAuth() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.Context.IsBasicAuth { ctx.Error(http.StatusUnauthorized, "reqBasicAuth", "basic auth required") @@ -212,7 +215,7 @@ func reqBasicAuth() macaron.Handler { } // reqSiteAdmin user should be the site admin -func reqSiteAdmin() macaron.Handler { +func reqSiteAdmin() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.IsUserSiteAdmin() { ctx.Error(http.StatusForbidden, "reqSiteAdmin", "user should be the site admin") @@ -222,7 +225,7 @@ func reqSiteAdmin() macaron.Handler { } // reqOwner user should be the owner of the repo or site admin. -func reqOwner() macaron.Handler { +func reqOwner() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.IsUserRepoOwner() && !ctx.IsUserSiteAdmin() { ctx.Error(http.StatusForbidden, "reqOwner", "user should be the owner of the repo") @@ -232,7 +235,7 @@ func reqOwner() macaron.Handler { } // reqAdmin user should be an owner or a collaborator with admin write of a repository, or site admin -func reqAdmin() macaron.Handler { +func reqAdmin() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() { ctx.Error(http.StatusForbidden, "reqAdmin", "user should be an owner or a collaborator with admin write of a repository") @@ -242,7 +245,7 @@ func reqAdmin() macaron.Handler { } // reqRepoWriter user should have a permission to write to a repo, or be a site admin -func reqRepoWriter(unitTypes ...models.UnitType) macaron.Handler { +func reqRepoWriter(unitTypes ...models.UnitType) func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.IsUserRepoWriter(unitTypes) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() { ctx.Error(http.StatusForbidden, "reqRepoWriter", "user should have a permission to write to a repo") @@ -252,7 +255,7 @@ func reqRepoWriter(unitTypes ...models.UnitType) macaron.Handler { } // reqRepoReader user should have specific read permission or be a repo admin or a site admin -func reqRepoReader(unitType models.UnitType) macaron.Handler { +func reqRepoReader(unitType models.UnitType) func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.IsUserRepoReaderSpecific(unitType) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() { ctx.Error(http.StatusForbidden, "reqRepoReader", "user should have specific read permission or be a repo admin or a site admin") @@ -262,7 +265,7 @@ func reqRepoReader(unitType models.UnitType) macaron.Handler { } // reqAnyRepoReader user should have any permission to read repository or permissions of site admin -func reqAnyRepoReader() macaron.Handler { +func reqAnyRepoReader() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.IsUserRepoReaderAny() && !ctx.IsUserSiteAdmin() { ctx.Error(http.StatusForbidden, "reqAnyRepoReader", "user should have any permission to read repository or permissions of site admin") @@ -272,7 +275,7 @@ func reqAnyRepoReader() macaron.Handler { } // reqOrgOwnership user should be an organization owner, or a site admin -func reqOrgOwnership() macaron.Handler { +func reqOrgOwnership() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if ctx.Context.IsUserSiteAdmin() { return @@ -304,7 +307,7 @@ func reqOrgOwnership() macaron.Handler { } // reqTeamMembership user should be an team member, or a site admin -func reqTeamMembership() macaron.Handler { +func reqTeamMembership() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if ctx.Context.IsUserSiteAdmin() { return @@ -341,7 +344,7 @@ func reqTeamMembership() macaron.Handler { } // reqOrgMembership user should be an organization member, or a site admin -func reqOrgMembership() macaron.Handler { +func reqOrgMembership() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if ctx.Context.IsUserSiteAdmin() { return @@ -371,7 +374,7 @@ func reqOrgMembership() macaron.Handler { } } -func reqGitHook() macaron.Handler { +func reqGitHook() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.User.CanEditGitHook() { ctx.Error(http.StatusForbidden, "", "must be allowed to edit Git hooks") @@ -380,7 +383,7 @@ func reqGitHook() macaron.Handler { } } -func orgAssignment(args ...bool) macaron.Handler { +func orgAssignment(args ...bool) func(ctx *context.APIContext) { var ( assignOrg bool assignTeam bool @@ -500,13 +503,6 @@ func mustEnableIssuesOrPulls(ctx *context.APIContext) { } } -func mustEnableUserHeatmap(ctx *context.APIContext) { - if !setting.Service.EnableUserHeatmap { - ctx.NotFound() - return - } -} - func mustNotBeArchived(ctx *context.APIContext) { if ctx.Repo.Repository.IsArchived { ctx.NotFound() @@ -514,18 +510,59 @@ func mustNotBeArchived(ctx *context.APIContext) { } } -// RegisterRoutes registers all v1 APIs routes to web application. -func RegisterRoutes(m *macaron.Macaron) { - bind := binding.Bind +// bind binding an obj to a func(ctx *context.APIContext) +func bind(obj interface{}) http.HandlerFunc { + var tp = reflect.TypeOf(obj) + for tp.Kind() == reflect.Ptr { + tp = tp.Elem() + } + return web.Wrap(func(ctx *context.APIContext) { + var theObj = reflect.New(tp).Interface() // create a new form obj for every request but not use obj directly + errs := binding.Bind(ctx.Req, theObj) + if len(errs) > 0 { + ctx.Error(422, "validationError", errs[0].Error()) + return + } + web.SetForm(ctx, theObj) + }) +} - if setting.API.EnableSwagger { - m.Get("/swagger", misc.Swagger) // Render V1 by default +// Routes registers all v1 APIs routes to web application. +func Routes() *web.Route { + var m = web.NewRoute() + + m.Use(session.Sessioner(session.Options{ + Provider: setting.SessionConfig.Provider, + ProviderConfig: setting.SessionConfig.ProviderConfig, + CookieName: setting.SessionConfig.CookieName, + CookiePath: setting.SessionConfig.CookiePath, + Gclifetime: setting.SessionConfig.Gclifetime, + Maxlifetime: setting.SessionConfig.Maxlifetime, + Secure: setting.SessionConfig.Secure, + Domain: setting.SessionConfig.Domain, + })) + m.Use(securityHeaders()) + if setting.CORSConfig.Enabled { + m.Use(cors.Handler(cors.Options{ + //Scheme: setting.CORSConfig.Scheme, // FIXME: the cors middleware needs scheme option + AllowedOrigins: setting.CORSConfig.AllowDomain, + //setting.CORSConfig.AllowSubdomain // FIXME: the cors middleware needs allowSubdomain option + AllowedMethods: setting.CORSConfig.Methods, + AllowCredentials: setting.CORSConfig.AllowCredentials, + MaxAge: int(setting.CORSConfig.MaxAge.Seconds()), + })) } + m.Use(context.APIContexter()) + m.Use(context.ToggleAPI(&context.ToggleOptions{ + SignInRequired: setting.Service.RequireSignInView, + })) - m.Group("/v1", func() { + m.Group("", func() { // Miscellaneous if setting.API.EnableSwagger { - m.Get("/swagger", misc.Swagger) + m.Get("/swagger", func(ctx *context.APIContext) { + ctx.Redirect("/api/swagger") + }) } m.Get("/version", misc.Version) m.Get("/signing-key.gpg", misc.SigningKey) @@ -544,7 +581,7 @@ func RegisterRoutes(m *macaron.Macaron) { Get(notify.ListNotifications). Put(notify.ReadNotifications) m.Get("/new", notify.NewAvailable) - m.Combo("/threads/:id"). + m.Combo("/threads/{id}"). Get(notify.GetThread). Patch(notify.ReadThread) }, reqToken()) @@ -553,28 +590,31 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/users", func() { m.Get("/search", user.Search) - m.Group("/:username", func() { + m.Group("/{username}", func() { m.Get("", user.GetInfo) - m.Get("/heatmap", mustEnableUserHeatmap, user.GetUserHeatmapData) + + if setting.Service.EnableUserHeatmap { + m.Get("/heatmap", user.GetUserHeatmapData) + } m.Get("/repos", user.ListUserRepos) m.Group("/tokens", func() { m.Combo("").Get(user.ListAccessTokens). Post(bind(api.CreateAccessTokenOption{}), user.CreateAccessToken) - m.Combo("/:id").Delete(user.DeleteAccessToken) + m.Combo("/{id}").Delete(user.DeleteAccessToken) }, reqBasicAuth()) }) }) m.Group("/users", func() { - m.Group("/:username", func() { + m.Group("/{username}", func() { m.Get("/keys", user.ListPublicKeys) m.Get("/gpg_keys", user.ListGPGKeys) m.Get("/followers", user.ListFollowers) m.Group("/following", func() { m.Get("", user.ListFollowing) - m.Get("/:target", user.CheckFollowing) + m.Get("/{target}", user.CheckFollowing) }) m.Get("/starred", user.GetStarredRepos) @@ -592,20 +632,20 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/followers", user.ListMyFollowers) m.Group("/following", func() { m.Get("", user.ListMyFollowing) - m.Combo("/:username").Get(user.CheckMyFollowing).Put(user.Follow).Delete(user.Unfollow) + m.Combo("/{username}").Get(user.CheckMyFollowing).Put(user.Follow).Delete(user.Unfollow) }) m.Group("/keys", func() { m.Combo("").Get(user.ListMyPublicKeys). Post(bind(api.CreateKeyOption{}), user.CreatePublicKey) - m.Combo("/:id").Get(user.GetPublicKey). + m.Combo("/{id}").Get(user.GetPublicKey). Delete(user.DeletePublicKey) }) m.Group("/applications", func() { m.Combo("/oauth2"). Get(user.ListOauth2Applications). Post(bind(api.CreateOAuth2ApplicationOptions{}), user.CreateOauth2Application) - m.Combo("/oauth2/:id"). + m.Combo("/oauth2/{id}"). Delete(user.DeleteOauth2Application). Patch(bind(api.CreateOAuth2ApplicationOptions{}), user.UpdateOauth2Application). Get(user.GetOauth2Application) @@ -614,7 +654,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/gpg_keys", func() { m.Combo("").Get(user.ListMyGPGKeys). Post(bind(api.CreateGPGKeyOption{}), user.CreateGPGKey) - m.Combo("/:id").Get(user.GetGPGKey). + m.Combo("/{id}").Get(user.GetGPGKey). Delete(user.DeleteGPGKey) }) @@ -623,7 +663,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/starred", func() { m.Get("", user.GetMyStarredRepos) - m.Group("/:username/:reponame", func() { + m.Group("/{username}/{reponame}", func() { m.Get("", user.IsStarring) m.Put("", user.Star) m.Delete("", user.Unstar) @@ -639,9 +679,9 @@ func RegisterRoutes(m *macaron.Macaron) { }, reqToken()) // Repositories - m.Post("/org/:org/repos", reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepoDeprecated) + m.Post("/org/{org}/repos", reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepoDeprecated) - m.Combo("/repositories/:id", reqToken()).Get(repo.GetByID) + m.Combo("/repositories/{id}", reqToken()).Get(repo.GetByID) m.Group("/repos", func() { m.Get("/search", repo.Search) @@ -650,10 +690,10 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/migrate", reqToken(), bind(api.MigrateRepoOptions{}), repo.Migrate) - m.Group("/:username/:reponame", func() { + m.Group("/{username}/{reponame}", func() { m.Combo("").Get(reqAnyRepoReader(), repo.Get). Delete(reqToken(), reqOwner(), repo.Delete). - Patch(reqToken(), reqAdmin(), context.RepoRefForAPI(), bind(api.EditRepoOption{}), repo.Edit) + Patch(reqToken(), reqAdmin(), context.RepoRefForAPI, bind(api.EditRepoOption{}), repo.Edit) m.Post("/transfer", reqOwner(), bind(api.TransferRepoOption{}), repo.Transfer) m.Combo("/notifications"). Get(reqToken(), notify.ListRepoNotifications). @@ -661,15 +701,15 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/hooks", func() { m.Combo("").Get(repo.ListHooks). Post(bind(api.CreateHookOption{}), repo.CreateHook) - m.Group("/:id", func() { + m.Group("/{id}", func() { m.Combo("").Get(repo.GetHook). Patch(bind(api.EditHookOption{}), repo.EditHook). Delete(repo.DeleteHook) - m.Post("/tests", context.RepoRefForAPI(), repo.TestHook) + m.Post("/tests", context.RepoRefForAPI, repo.TestHook) }) m.Group("/git", func() { m.Combo("").Get(repo.ListGitHooks) - m.Group("/:id", func() { + m.Group("/{id}", func() { m.Combo("").Get(repo.GetGitHook). Patch(bind(api.EditGitHookOption{}), repo.EditGitHook). Delete(repo.DeleteGitHook) @@ -678,11 +718,11 @@ func RegisterRoutes(m *macaron.Macaron) { }, reqToken(), reqAdmin()) m.Group("/collaborators", func() { m.Get("", reqAnyRepoReader(), repo.ListCollaborators) - m.Combo("/:collaborator").Get(reqAnyRepoReader(), repo.IsCollaborator). + m.Combo("/{collaborator}").Get(reqAnyRepoReader(), repo.IsCollaborator). Put(reqAdmin(), bind(api.AddCollaboratorOption{}), repo.AddCollaborator). Delete(reqAdmin(), repo.DeleteCollaborator) }, reqToken()) - m.Get("/raw/*", context.RepoRefForAPI(), reqRepoReader(models.UnitTypeCode), repo.GetRawFile) + m.Get("/raw/*", context.RepoRefForAPI, reqRepoReader(models.UnitTypeCode), repo.GetRawFile) m.Get("/archive/*", reqRepoReader(models.UnitTypeCode), repo.GetArchive) m.Combo("/forks").Get(repo.ListForks). Post(reqToken(), reqRepoReader(models.UnitTypeCode), bind(api.CreateForkOption{}), repo.CreateFork) @@ -695,7 +735,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/branch_protections", func() { m.Get("", repo.ListBranchProtections) m.Post("", bind(api.CreateBranchProtectionOption{}), repo.CreateBranchProtection) - m.Group("/:name", func() { + m.Group("/{name}", func() { m.Get("", repo.GetBranchProtection) m.Patch("", bind(api.EditBranchProtectionOption{}), repo.EditBranchProtection) m.Delete("", repo.DeleteBranchProtection) @@ -707,19 +747,19 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/keys", func() { m.Combo("").Get(repo.ListDeployKeys). Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey) - m.Combo("/:id").Get(repo.GetDeployKey). + m.Combo("/{id}").Get(repo.GetDeployKey). Delete(repo.DeleteDeploykey) }, reqToken(), reqAdmin()) m.Group("/times", func() { m.Combo("").Get(repo.ListTrackedTimesByRepository) - m.Combo("/:timetrackingusername").Get(repo.ListTrackedTimesByUser) + m.Combo("/{timetrackingusername}").Get(repo.ListTrackedTimesByUser) }, mustEnableIssues, reqToken()) m.Group("/issues", func() { m.Combo("").Get(repo.ListIssues). Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue) m.Group("/comments", func() { m.Get("", repo.ListRepoIssueComments) - m.Group("/:id", func() { + m.Group("/{id}", func() { m.Combo(""). Get(repo.GetIssueComment). Patch(mustNotBeArchived, reqToken(), bind(api.EditIssueCommentOption{}), repo.EditIssueComment). @@ -730,13 +770,13 @@ func RegisterRoutes(m *macaron.Macaron) { Delete(reqToken(), bind(api.EditReactionOption{}), repo.DeleteIssueCommentReaction) }) }) - m.Group("/:index", func() { + m.Group("/{index}", func() { m.Combo("").Get(repo.GetIssue). Patch(reqToken(), bind(api.EditIssueOption{}), repo.EditIssue) m.Group("/comments", func() { m.Combo("").Get(repo.ListIssueComments). Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment) - m.Combo("/:id", reqToken()).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). + m.Combo("/{id}", reqToken()).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). Delete(repo.DeleteIssueCommentDeprecated) }) m.Group("/labels", func() { @@ -744,14 +784,14 @@ func RegisterRoutes(m *macaron.Macaron) { Post(reqToken(), bind(api.IssueLabelsOption{}), repo.AddIssueLabels). Put(reqToken(), bind(api.IssueLabelsOption{}), repo.ReplaceIssueLabels). Delete(reqToken(), repo.ClearIssueLabels) - m.Delete("/:id", reqToken(), repo.DeleteIssueLabel) + m.Delete("/{id}", reqToken(), repo.DeleteIssueLabel) }) m.Group("/times", func() { m.Combo(""). Get(repo.ListTrackedTimes). Post(bind(api.AddTimeOption{}), repo.AddTime). Delete(repo.ResetIssueTime) - m.Delete("/:id", repo.DeleteTime) + m.Delete("/{id}", repo.DeleteTime) }, reqToken()) m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline) m.Group("/stopwatch", func() { @@ -762,8 +802,8 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/subscriptions", func() { m.Get("", repo.GetIssueSubscribers) m.Get("/check", reqToken(), repo.CheckIssueSubscription) - m.Put("/:user", reqToken(), repo.AddIssueSubscription) - m.Delete("/:user", reqToken(), repo.DelIssueSubscription) + m.Put("/{user}", reqToken(), repo.AddIssueSubscription) + m.Delete("/{user}", reqToken(), repo.DelIssueSubscription) }) m.Combo("/reactions"). Get(repo.GetIssueReactions). @@ -774,7 +814,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/labels", func() { m.Combo("").Get(repo.ListLabels). Post(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.CreateLabelOption{}), repo.CreateLabel) - m.Combo("/:id").Get(repo.GetLabel). + m.Combo("/{id}").Get(repo.GetLabel). Patch(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.EditLabelOption{}), repo.EditLabel). Delete(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), repo.DeleteLabel) }) @@ -783,7 +823,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/milestones", func() { m.Combo("").Get(repo.ListMilestones). Post(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.CreateMilestoneOption{}), repo.CreateMilestone) - m.Combo("/:id").Get(repo.GetMilestone). + m.Combo("/{id}").Get(repo.GetMilestone). Patch(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone). Delete(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), repo.DeleteMilestone) }) @@ -797,30 +837,30 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/releases", func() { m.Combo("").Get(repo.ListReleases). Post(reqToken(), reqRepoWriter(models.UnitTypeReleases), context.ReferencesGitRepo(false), bind(api.CreateReleaseOption{}), repo.CreateRelease) - m.Group("/:id", func() { + m.Group("/{id}", func() { m.Combo("").Get(repo.GetRelease). Patch(reqToken(), reqRepoWriter(models.UnitTypeReleases), context.ReferencesGitRepo(false), bind(api.EditReleaseOption{}), repo.EditRelease). Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteRelease) m.Group("/assets", func() { m.Combo("").Get(repo.ListReleaseAttachments). Post(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.CreateReleaseAttachment) - m.Combo("/:asset").Get(repo.GetReleaseAttachment). + m.Combo("/{asset}").Get(repo.GetReleaseAttachment). Patch(reqToken(), reqRepoWriter(models.UnitTypeReleases), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment). Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteReleaseAttachment) }) }) m.Group("/tags", func() { - m.Combo("/:tag"). + m.Combo("/{tag}"). Get(repo.GetReleaseTag). Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteReleaseTag) }) }, reqRepoReader(models.UnitTypeReleases)) m.Post("/mirror-sync", reqToken(), reqRepoWriter(models.UnitTypeCode), repo.MirrorSync) - m.Get("/editorconfig/:filename", context.RepoRefForAPI(), reqRepoReader(models.UnitTypeCode), repo.GetEditorconfig) + m.Get("/editorconfig/{filename}", context.RepoRefForAPI, reqRepoReader(models.UnitTypeCode), repo.GetEditorconfig) m.Group("/pulls", func() { - m.Combo("").Get(bind(api.ListPullRequestsOptions{}), repo.ListPullRequests). + m.Combo("").Get(repo.ListPullRequests). Post(reqToken(), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest) - m.Group("/:index", func() { + m.Group("/{index}", func() { m.Combo("").Get(repo.GetPullRequest). Patch(reqToken(), reqRepoWriter(models.UnitTypePullRequests), bind(api.EditPullRequestOption{}), repo.EditPullRequest) m.Get(".diff", repo.DownloadPullDiff) @@ -832,7 +872,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Combo(""). Get(repo.ListPullReviews). Post(reqToken(), bind(api.CreatePullReviewOptions{}), repo.CreatePullReview) - m.Group("/:id", func() { + m.Group("/{id}", func() { m.Combo(""). Get(repo.GetPullReview). Delete(reqToken(), repo.DeletePullReview). @@ -847,25 +887,25 @@ func RegisterRoutes(m *macaron.Macaron) { }) }, mustAllowPulls, reqRepoReader(models.UnitTypeCode), context.ReferencesGitRepo(false)) m.Group("/statuses", func() { - m.Combo("/:sha").Get(repo.GetCommitStatuses). + m.Combo("/{sha}").Get(repo.GetCommitStatuses). Post(reqToken(), bind(api.CreateStatusOption{}), repo.NewCommitStatus) }, reqRepoReader(models.UnitTypeCode)) m.Group("/commits", func() { m.Get("", repo.GetAllCommits) - m.Group("/:ref", func() { + m.Group("/{ref}", func() { m.Get("/status", repo.GetCombinedCommitStatusByRef) m.Get("/statuses", repo.GetCommitStatusesByRef) }) }, reqRepoReader(models.UnitTypeCode)) m.Group("/git", func() { m.Group("/commits", func() { - m.Get("/:sha", repo.GetSingleCommit) + m.Get("/{sha}", repo.GetSingleCommit) }) m.Get("/refs", repo.GetGitAllRefs) m.Get("/refs/*", repo.GetGitRefs) - m.Get("/trees/:sha", context.RepoRefForAPI(), repo.GetTree) - m.Get("/blobs/:sha", context.RepoRefForAPI(), repo.GetBlob) - m.Get("/tags/:sha", context.RepoRefForAPI(), repo.GetTag) + m.Get("/trees/{sha}", context.RepoRefForAPI, repo.GetTree) + m.Get("/blobs/{sha}", context.RepoRefForAPI, repo.GetBlob) + m.Get("/tags/{sha}", context.RepoRefForAPI, repo.GetTag) }, reqRepoReader(models.UnitTypeCode)) m.Group("/contents", func() { m.Get("", repo.GetContentsList) @@ -880,7 +920,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/topics", func() { m.Combo("").Get(repo.ListTopics). Put(reqToken(), reqAdmin(), bind(api.RepoTopicOptions{}), repo.UpdateTopics) - m.Group("/:topic", func() { + m.Group("/{topic}", func() { m.Combo("").Put(reqToken(), repo.AddTopic). Delete(reqToken(), repo.DeleteTopic) }, reqAdmin()) @@ -892,10 +932,10 @@ func RegisterRoutes(m *macaron.Macaron) { // Organizations m.Get("/user/orgs", reqToken(), org.ListMyOrgs) - m.Get("/users/:username/orgs", org.ListUserOrgs) + m.Get("/users/{username}/orgs", org.ListUserOrgs) m.Post("/orgs", reqToken(), bind(api.CreateOrgOption{}), org.Create) m.Get("/orgs", org.GetAll) - m.Group("/orgs/:org", func() { + m.Group("/orgs/{org}", func() { m.Combo("").Get(org.Get). Patch(reqToken(), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit). Delete(reqToken(), reqOrgOwnership(), org.Delete) @@ -903,12 +943,12 @@ func RegisterRoutes(m *macaron.Macaron) { Post(reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepo) m.Group("/members", func() { m.Get("", org.ListMembers) - m.Combo("/:username").Get(org.IsMember). + m.Combo("/{username}").Get(org.IsMember). Delete(reqToken(), reqOrgOwnership(), org.DeleteMember) }) m.Group("/public_members", func() { m.Get("", org.ListPublicMembers) - m.Combo("/:username").Get(org.IsPublicMember). + m.Combo("/{username}").Get(org.IsPublicMember). Put(reqToken(), reqOrgMembership(), org.PublicizeMember). Delete(reqToken(), reqOrgMembership(), org.ConcealMember) }) @@ -920,56 +960,52 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/labels", func() { m.Get("", org.ListLabels) m.Post("", reqToken(), reqOrgOwnership(), bind(api.CreateLabelOption{}), org.CreateLabel) - m.Combo("/:id").Get(org.GetLabel). + m.Combo("/{id}").Get(org.GetLabel). Patch(reqToken(), reqOrgOwnership(), bind(api.EditLabelOption{}), org.EditLabel). Delete(reqToken(), reqOrgOwnership(), org.DeleteLabel) }) m.Group("/hooks", func() { m.Combo("").Get(org.ListHooks). Post(bind(api.CreateHookOption{}), org.CreateHook) - m.Combo("/:id").Get(org.GetHook). + m.Combo("/{id}").Get(org.GetHook). Patch(bind(api.EditHookOption{}), org.EditHook). Delete(org.DeleteHook) }, reqToken(), reqOrgOwnership()) }, orgAssignment(true)) - m.Group("/teams/:teamid", func() { + m.Group("/teams/{teamid}", func() { m.Combo("").Get(org.GetTeam). Patch(reqOrgOwnership(), bind(api.EditTeamOption{}), org.EditTeam). Delete(reqOrgOwnership(), org.DeleteTeam) m.Group("/members", func() { m.Get("", org.GetTeamMembers) - m.Combo("/:username"). + m.Combo("/{username}"). Get(org.GetTeamMember). Put(reqOrgOwnership(), org.AddTeamMember). Delete(reqOrgOwnership(), org.RemoveTeamMember) }) m.Group("/repos", func() { m.Get("", org.GetTeamRepos) - m.Combo("/:org/:reponame"). + m.Combo("/{org}/{reponame}"). Put(org.AddTeamRepository). Delete(org.RemoveTeamRepository) }) }, orgAssignment(false, true), reqToken(), reqTeamMembership()) - m.Any("/*", func(ctx *context.APIContext) { - ctx.NotFound() - }) - m.Group("/admin", func() { m.Group("/cron", func() { m.Get("", admin.ListCronTasks) - m.Post("/:task", admin.PostCronTask) + m.Post("/{task}", admin.PostCronTask) }) m.Get("/orgs", admin.GetAllOrgs) m.Group("/users", func() { m.Get("", admin.GetAllUsers) m.Post("", bind(api.CreateUserOption{}), admin.CreateUser) - m.Group("/:username", func() { + m.Group("/{username}", func() { m.Combo("").Patch(bind(api.EditUserOption{}), admin.EditUser). Delete(admin.DeleteUser) m.Group("/keys", func() { m.Post("", bind(api.CreateKeyOption{}), admin.CreatePublicKey) - m.Delete("/:id", admin.DeleteUserPublicKey) + m.Delete("/{id}", admin.DeleteUserPublicKey) }) m.Get("/orgs", org.ListUserOrgs) m.Post("/orgs", bind(api.CreateOrgOption{}), admin.CreateOrg) @@ -978,23 +1014,26 @@ func RegisterRoutes(m *macaron.Macaron) { }) m.Group("/unadopted", func() { m.Get("", admin.ListUnadoptedRepositories) - m.Post("/:username/:reponame", admin.AdoptRepository) - m.Delete("/:username/:reponame", admin.DeleteUnadoptedRepository) + m.Post("/{username}/{reponame}", admin.AdoptRepository) + m.Delete("/{username}/{reponame}", admin.DeleteUnadoptedRepository) }) }, reqToken(), reqSiteAdmin()) m.Group("/topics", func() { m.Get("/search", repo.TopicSearch) }) - }, securityHeaders(), context.APIContexter(), sudo()) + }, sudo()) + + return m } -func securityHeaders() macaron.Handler { - return func(ctx *macaron.Context) { - ctx.Resp.Before(func(w macaron.ResponseWriter) { +func securityHeaders() func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { // CORB: https://www.chromium.org/Home/chromium-security/corb-for-developers // http://stackoverflow.com/a/3146618/244009 - w.Header().Set("x-content-type-options", "nosniff") + resp.Header().Set("x-content-type-options", "nosniff") + next.ServeHTTP(resp, req) }) } } diff --git a/routers/api/v1/misc/markdown.go b/routers/api/v1/misc/markdown.go index a10c288df4..5718185309 100644 --- a/routers/api/v1/misc/markdown.go +++ b/routers/api/v1/misc/markdown.go @@ -5,6 +5,7 @@ package misc import ( + "io/ioutil" "net/http" "strings" @@ -13,12 +14,13 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "mvdan.cc/xurls/v2" ) // Markdown render markdown document to HTML -func Markdown(ctx *context.APIContext, form api.MarkdownOption) { +func Markdown(ctx *context.APIContext) { // swagger:operation POST /markdown miscellaneous renderMarkdown // --- // summary: Render a markdown document as HTML @@ -37,6 +39,8 @@ func Markdown(ctx *context.APIContext, form api.MarkdownOption) { // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.MarkdownOption) + if ctx.HasAPIError() { ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg()) return @@ -117,7 +121,7 @@ func MarkdownRaw(ctx *context.APIContext) { // "422": // "$ref": "#/responses/validationError" - body, err := ctx.Req.Body().Bytes() + body, err := ioutil.ReadAll(ctx.Req.Body) if err != nil { ctx.Error(http.StatusUnprocessableEntity, "", err) return diff --git a/routers/api/v1/misc/markdown_test.go b/routers/api/v1/misc/markdown_test.go index 6c81ec8eb4..3ae3165fd3 100644 --- a/routers/api/v1/misc/markdown_test.go +++ b/routers/api/v1/misc/markdown_test.go @@ -15,10 +15,10 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" - "gitea.com/macaron/inject" - "gitea.com/macaron/macaron" "github.com/stretchr/testify/assert" ) @@ -26,25 +26,21 @@ const AppURL = "http://localhost:3000/" const Repo = "gogits/gogs" const AppSubURL = AppURL + Repo + "/" -func createContext(req *http.Request) (*macaron.Context, *httptest.ResponseRecorder) { +func createContext(req *http.Request) (*context.Context, *httptest.ResponseRecorder) { + var rnd = templates.HTMLRenderer() resp := httptest.NewRecorder() - c := &macaron.Context{ - Injector: inject.New(), - Req: macaron.Request{Request: req}, - Resp: macaron.NewResponseWriter(req.Method, resp), - Render: &macaron.DummyRender{ResponseWriter: resp}, - Data: make(map[string]interface{}), + c := &context.Context{ + Req: req, + Resp: context.NewResponse(resp), + Render: rnd, + Data: make(map[string]interface{}), } - c.Map(c) - c.Map(req) return c, resp } -func wrap(ctx *macaron.Context) *context.APIContext { +func wrap(ctx *context.Context) *context.APIContext { return &context.APIContext{ - Context: &context.Context{ - Context: ctx, - }, + Context: ctx, } } @@ -115,7 +111,8 @@ Here are some links to the most important topics. You can find the full list of for i := 0; i < len(testCases); i += 2 { options.Text = testCases[i] - Markdown(ctx, options) + web.SetForm(ctx, &options) + Markdown(ctx) assert.Equal(t, testCases[i+1], resp.Body.String()) resp.Body.Reset() } @@ -156,7 +153,8 @@ func TestAPI_RenderSimple(t *testing.T) { for i := 0; i < len(simpleCases); i += 2 { options.Text = simpleCases[i] - Markdown(ctx, options) + web.SetForm(ctx, &options) + Markdown(ctx) assert.Equal(t, simpleCases[i+1], resp.Body.String()) resp.Body.Reset() } @@ -174,7 +172,7 @@ func TestAPI_RenderRaw(t *testing.T) { ctx := wrap(m) for i := 0; i < len(simpleCases); i += 2 { - ctx.Req.Request.Body = ioutil.NopCloser(strings.NewReader(simpleCases[i])) + ctx.Req.Body = ioutil.NopCloser(strings.NewReader(simpleCases[i])) MarkdownRaw(ctx) assert.Equal(t, simpleCases[i+1], resp.Body.String()) resp.Body.Reset() diff --git a/routers/api/v1/org/hook.go b/routers/api/v1/org/hook.go index cce959bb49..ed827c48d4 100644 --- a/routers/api/v1/org/hook.go +++ b/routers/api/v1/org/hook.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -85,7 +86,7 @@ func GetHook(ctx *context.APIContext) { } // CreateHook create a hook for an organization -func CreateHook(ctx *context.APIContext, form api.CreateHookOption) { +func CreateHook(ctx *context.APIContext) { // swagger:operation POST /orgs/{org}/hooks/ organization orgCreateHook // --- // summary: Create a hook @@ -108,15 +109,16 @@ func CreateHook(ctx *context.APIContext, form api.CreateHookOption) { // "201": // "$ref": "#/responses/Hook" + form := web.GetForm(ctx).(*api.CreateHookOption) //TODO in body params - if !utils.CheckCreateHookOption(ctx, &form) { + if !utils.CheckCreateHookOption(ctx, form) { return } - utils.AddOrgHook(ctx, &form) + utils.AddOrgHook(ctx, form) } // EditHook modify a hook of a repository -func EditHook(ctx *context.APIContext, form api.EditHookOption) { +func EditHook(ctx *context.APIContext) { // swagger:operation PATCH /orgs/{org}/hooks/{id} organization orgEditHook // --- // summary: Update a hook @@ -144,9 +146,11 @@ func EditHook(ctx *context.APIContext, form api.EditHookOption) { // "200": // "$ref": "#/responses/Hook" + form := web.GetForm(ctx).(*api.EditHookOption) + //TODO in body params hookID := ctx.ParamsInt64(":id") - utils.EditOrgHook(ctx, &form, hookID) + utils.EditOrgHook(ctx, form, hookID) } // DeleteHook delete a hook of an organization diff --git a/routers/api/v1/org/label.go b/routers/api/v1/org/label.go index 9a8a4fa291..76061f163a 100644 --- a/routers/api/v1/org/label.go +++ b/routers/api/v1/org/label.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -52,7 +53,7 @@ func ListLabels(ctx *context.APIContext) { } // CreateLabel create a label for a repository -func CreateLabel(ctx *context.APIContext, form api.CreateLabelOption) { +func CreateLabel(ctx *context.APIContext) { // swagger:operation POST /orgs/{org}/labels organization orgCreateLabel // --- // summary: Create a label for an organization @@ -75,7 +76,7 @@ func CreateLabel(ctx *context.APIContext, form api.CreateLabelOption) { // "$ref": "#/responses/Label" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.CreateLabelOption) form.Color = strings.Trim(form.Color, " ") if len(form.Color) == 6 { form.Color = "#" + form.Color @@ -144,7 +145,7 @@ func GetLabel(ctx *context.APIContext) { } // EditLabel modify a label for an Organization -func EditLabel(ctx *context.APIContext, form api.EditLabelOption) { +func EditLabel(ctx *context.APIContext) { // swagger:operation PATCH /orgs/{org}/labels/{id} organization orgEditLabel // --- // summary: Update a label @@ -173,7 +174,7 @@ func EditLabel(ctx *context.APIContext, form api.EditLabelOption) { // "$ref": "#/responses/Label" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.EditLabelOption) label, err := models.GetLabelInOrgByID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id")) if err != nil { if models.IsErrOrgLabelNotExist(err) { diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index ca3e10173b..61e09e1126 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -149,7 +150,7 @@ func GetAll(ctx *context.APIContext) { } // Create api for create organization -func Create(ctx *context.APIContext, form api.CreateOrgOption) { +func Create(ctx *context.APIContext) { // swagger:operation POST /orgs organization orgCreate // --- // summary: Create an organization @@ -169,7 +170,7 @@ func Create(ctx *context.APIContext, form api.CreateOrgOption) { // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.CreateOrgOption) if !ctx.User.CanCreateOrganization() { ctx.Error(http.StatusForbidden, "Create organization not allowed", nil) return @@ -231,7 +232,7 @@ func Get(ctx *context.APIContext) { } // Edit change an organization's information -func Edit(ctx *context.APIContext, form api.EditOrgOption) { +func Edit(ctx *context.APIContext) { // swagger:operation PATCH /orgs/{org} organization orgEdit // --- // summary: Edit an organization @@ -253,7 +254,7 @@ func Edit(ctx *context.APIContext, form api.EditOrgOption) { // responses: // "200": // "$ref": "#/responses/Organization" - + form := web.GetForm(ctx).(*api.EditOrgOption) org := ctx.Org.Organization org.FullName = form.FullName org.Description = form.Description diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index 72387dcbf7..c749751ac4 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -131,7 +132,7 @@ func GetTeam(ctx *context.APIContext) { } // CreateTeam api for create a team -func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) { +func CreateTeam(ctx *context.APIContext) { // swagger:operation POST /orgs/{org}/teams organization orgCreateTeam // --- // summary: Create a team @@ -154,7 +155,7 @@ func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) { // "$ref": "#/responses/Team" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.CreateTeamOption) team := &models.Team{ OrgID: ctx.Org.Organization.ID, Name: form.Name, @@ -190,7 +191,7 @@ func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) { } // EditTeam api for edit a team -func EditTeam(ctx *context.APIContext, form api.EditTeamOption) { +func EditTeam(ctx *context.APIContext) { // swagger:operation PATCH /teams/{id} organization orgEditTeam // --- // summary: Edit a team @@ -212,6 +213,8 @@ func EditTeam(ctx *context.APIContext, form api.EditTeamOption) { // "200": // "$ref": "#/responses/Team" + form := web.GetForm(ctx).(*api.EditTeamOption) + team := ctx.Org.Team if err := team.GetUnits(); err != nil { ctx.InternalServerError(err) diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 9a0a552bad..790464c8bc 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" pull_service "code.gitea.io/gitea/services/pull" repo_service "code.gitea.io/gitea/services/repository" ) @@ -175,7 +176,7 @@ func DeleteBranch(ctx *context.APIContext) { } // CreateBranch creates a branch for a user's repository -func CreateBranch(ctx *context.APIContext, opt api.CreateBranchRepoOption) { +func CreateBranch(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/branches repository repoCreateBranch // --- // summary: Create a branch @@ -206,6 +207,7 @@ func CreateBranch(ctx *context.APIContext, opt api.CreateBranchRepoOption) { // "409": // description: The branch with the same name already exists. + opt := web.GetForm(ctx).(*api.CreateBranchRepoOption) if ctx.Repo.Repository.IsEmpty { ctx.Error(http.StatusNotFound, "", "Git Repository is empty.") return @@ -395,7 +397,7 @@ func ListBranchProtections(ctx *context.APIContext) { } // CreateBranchProtection creates a branch protection for a repo -func CreateBranchProtection(ctx *context.APIContext, form api.CreateBranchProtectionOption) { +func CreateBranchProtection(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/branch_protections repository repoCreateBranchProtection // --- // summary: Create a branch protections for a repository @@ -428,6 +430,7 @@ func CreateBranchProtection(ctx *context.APIContext, form api.CreateBranchProtec // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.CreateBranchProtectionOption) repo := ctx.Repo.Repository // Currently protection must match an actual branch @@ -561,7 +564,7 @@ func CreateBranchProtection(ctx *context.APIContext, form api.CreateBranchProtec } // EditBranchProtection edits a branch protection for a repo -func EditBranchProtection(ctx *context.APIContext, form api.EditBranchProtectionOption) { +func EditBranchProtection(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/branch_protections/{name} repository repoEditBranchProtection // --- // summary: Edit a branch protections for a repository. Only fields that are set will be changed @@ -596,7 +599,7 @@ func EditBranchProtection(ctx *context.APIContext, form api.EditBranchProtection // "$ref": "#/responses/notFound" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.EditBranchProtectionOption) repo := ctx.Repo.Repository bpName := ctx.Params(":name") protectBranch, err := models.GetProtectedBranchBy(repo.ID, bpName) diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go index 497255a474..a4fc1d8f11 100644 --- a/routers/api/v1/repo/collaborators.go +++ b/routers/api/v1/repo/collaborators.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -111,7 +112,7 @@ func IsCollaborator(ctx *context.APIContext) { } // AddCollaborator add a collaborator to a repository -func AddCollaborator(ctx *context.APIContext, form api.AddCollaboratorOption) { +func AddCollaborator(ctx *context.APIContext) { // swagger:operation PUT /repos/{owner}/{repo}/collaborators/{collaborator} repository repoAddCollaborator // --- // summary: Add a collaborator to a repository @@ -143,6 +144,8 @@ func AddCollaborator(ctx *context.APIContext, form api.AddCollaboratorOption) { // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.AddCollaboratorOption) + collaborator, err := models.GetUserByName(ctx.Params(":collaborator")) if err != nil { if models.IsErrUserNotExist(err) { diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go index 8c877285a8..a16cca0f4e 100644 --- a/routers/api/v1/repo/commits.go +++ b/routers/api/v1/repo/commits.go @@ -69,7 +69,11 @@ func getCommit(ctx *context.APIContext, identifier string) { defer gitRepo.Close() commit, err := gitRepo.GetCommit(identifier) if err != nil { - ctx.NotFoundOrServerError("GetCommit", git.IsErrNotExist, err) + if git.IsErrNotExist(err) { + ctx.NotFound(identifier) + return + } + ctx.Error(http.StatusInternalServerError, "gitRepo.GetCommit", err) return } diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index bc85fec630..044d0fe565 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/repofiles" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/repo" ) @@ -167,7 +168,7 @@ func canReadFiles(r *context.Repository) bool { } // CreateFile handles API call for creating a file -func CreateFile(ctx *context.APIContext, apiOpts api.CreateFileOptions) { +func CreateFile(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/contents/{filepath} repository repoCreateFile // --- // summary: Create a file in a repository @@ -206,6 +207,7 @@ func CreateFile(ctx *context.APIContext, apiOpts api.CreateFileOptions) { // "422": // "$ref": "#/responses/error" + apiOpts := web.GetForm(ctx).(*api.CreateFileOptions) if ctx.Repo.Repository.IsEmpty { ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", fmt.Errorf("repo is empty")) } @@ -253,7 +255,7 @@ func CreateFile(ctx *context.APIContext, apiOpts api.CreateFileOptions) { } // UpdateFile handles API call for updating a file -func UpdateFile(ctx *context.APIContext, apiOpts api.UpdateFileOptions) { +func UpdateFile(ctx *context.APIContext) { // swagger:operation PUT /repos/{owner}/{repo}/contents/{filepath} repository repoUpdateFile // --- // summary: Update a file in a repository @@ -291,7 +293,7 @@ func UpdateFile(ctx *context.APIContext, apiOpts api.UpdateFileOptions) { // "$ref": "#/responses/notFound" // "422": // "$ref": "#/responses/error" - + apiOpts := web.GetForm(ctx).(*api.UpdateFileOptions) if ctx.Repo.Repository.IsEmpty { ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", fmt.Errorf("repo is empty")) } @@ -377,7 +379,7 @@ func createOrUpdateFile(ctx *context.APIContext, opts *repofiles.UpdateRepoFileO } // DeleteFile Delete a fle in a repository -func DeleteFile(ctx *context.APIContext, apiOpts api.DeleteFileOptions) { +func DeleteFile(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo}/contents/{filepath} repository repoDeleteFile // --- // summary: Delete a file in a repository @@ -416,6 +418,7 @@ func DeleteFile(ctx *context.APIContext, apiOpts api.DeleteFileOptions) { // "404": // "$ref": "#/responses/error" + apiOpts := web.GetForm(ctx).(*api.DeleteFileOptions) if !canWriteFiles(ctx.Repo) { ctx.Error(http.StatusForbidden, "DeleteFile", models.ErrUserDoesNotHaveAccessToRepo{ UserID: ctx.User.ID, diff --git a/routers/api/v1/repo/fork.go b/routers/api/v1/repo/fork.go index 24700f7a7f..55fcefaccc 100644 --- a/routers/api/v1/repo/fork.go +++ b/routers/api/v1/repo/fork.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" repo_service "code.gitea.io/gitea/services/repository" ) @@ -65,7 +66,7 @@ func ListForks(ctx *context.APIContext) { } // CreateFork create a fork of a repo -func CreateFork(ctx *context.APIContext, form api.CreateForkOption) { +func CreateFork(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/forks repository createFork // --- // summary: Fork a repository @@ -94,6 +95,7 @@ func CreateFork(ctx *context.APIContext, form api.CreateForkOption) { // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.CreateForkOption) repo := ctx.Repo.Repository var forker *models.User // user/org that will own the fork if form.Organization == nil { diff --git a/routers/api/v1/repo/git_hook.go b/routers/api/v1/repo/git_hook.go index 0c538ac6b3..0b4e09cadd 100644 --- a/routers/api/v1/repo/git_hook.go +++ b/routers/api/v1/repo/git_hook.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/git" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" ) // ListGitHooks list all Git hooks of a repository @@ -91,7 +92,7 @@ func GetGitHook(ctx *context.APIContext) { } // EditGitHook modify a Git hook of a repository -func EditGitHook(ctx *context.APIContext, form api.EditGitHookOption) { +func EditGitHook(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/hooks/git/{id} repository repoEditGitHook // --- // summary: Edit a Git hook in a repository @@ -123,6 +124,7 @@ func EditGitHook(ctx *context.APIContext, form api.EditGitHookOption) { // "404": // "$ref": "#/responses/notFound" + form := web.GetForm(ctx).(*api.EditGitHookOption) hookID := ctx.Params(":id") hook, err := ctx.Repo.GitRepo.GetHook(hookID) if err != nil { diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go index 575b1fc480..520a7a0202 100644 --- a/routers/api/v1/repo/hook.go +++ b/routers/api/v1/repo/hook.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/git" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/webhook" ) @@ -158,7 +159,7 @@ func TestHook(ctx *context.APIContext) { } // CreateHook create a hook for a repository -func CreateHook(ctx *context.APIContext, form api.CreateHookOption) { +func CreateHook(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/hooks repository repoCreateHook // --- // summary: Create a hook @@ -184,14 +185,16 @@ func CreateHook(ctx *context.APIContext, form api.CreateHookOption) { // responses: // "201": // "$ref": "#/responses/Hook" - if !utils.CheckCreateHookOption(ctx, &form) { + form := web.GetForm(ctx).(*api.CreateHookOption) + + if !utils.CheckCreateHookOption(ctx, form) { return } - utils.AddRepoHook(ctx, &form) + utils.AddRepoHook(ctx, form) } // EditHook modify a hook of a repository -func EditHook(ctx *context.APIContext, form api.EditHookOption) { +func EditHook(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/hooks/{id} repository repoEditHook // --- // summary: Edit a hook in a repository @@ -221,8 +224,9 @@ func EditHook(ctx *context.APIContext, form api.EditHookOption) { // responses: // "200": // "$ref": "#/responses/Hook" + form := web.GetForm(ctx).(*api.EditHookOption) hookID := ctx.ParamsInt64(":id") - utils.EditRepoHook(ctx, &form, hookID) + utils.EditRepoHook(ctx, form, hookID) } // DeleteHook delete a hook of a repository diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index bab8f373ce..17dad97945 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -22,6 +22,7 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" issue_service "code.gitea.io/gitea/services/issue" ) @@ -448,7 +449,7 @@ func GetIssue(ctx *context.APIContext) { } // CreateIssue create an issue of a repository -func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) { +func CreateIssue(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/issues issue issueCreateIssue // --- // summary: Create an issue. If using deadline only the date will be taken into account, and time of day ignored. @@ -480,7 +481,7 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) { // "$ref": "#/responses/error" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.CreateIssueOption) var deadlineUnix timeutil.TimeStamp if form.Deadline != nil && ctx.Repo.CanWrite(models.UnitTypeIssues) { deadlineUnix = timeutil.TimeStamp(form.Deadline.Unix()) @@ -564,7 +565,7 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) { } // EditIssue modify an issue of a repository -func EditIssue(ctx *context.APIContext, form api.EditIssueOption) { +func EditIssue(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/issues/{index} issue issueEditIssue // --- // summary: Edit an issue. If using deadline only the date will be taken into account, and time of day ignored. @@ -603,6 +604,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) { // "412": // "$ref": "#/responses/error" + form := web.GetForm(ctx).(*api.EditIssueOption) issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrIssueNotExist(err) { @@ -723,7 +725,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) { } // UpdateIssueDeadline updates an issue deadline -func UpdateIssueDeadline(ctx *context.APIContext, form api.EditDeadlineOption) { +func UpdateIssueDeadline(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/issues/{index}/deadline issue issueEditIssueDeadline // --- // summary: Set an issue deadline. If set to null, the deadline is deleted. If using deadline only the date will be taken into account, and time of day ignored. @@ -759,7 +761,7 @@ func UpdateIssueDeadline(ctx *context.APIContext, form api.EditDeadlineOption) { // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" - + form := web.GetForm(ctx).(*api.EditDeadlineOption) issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrIssueNotExist(err) { diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go index af69ae981a..d62ca81314 100644 --- a/routers/api/v1/repo/issue_comment.go +++ b/routers/api/v1/repo/issue_comment.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" comment_service "code.gitea.io/gitea/services/comments" ) @@ -174,7 +175,7 @@ func ListRepoIssueComments(ctx *context.APIContext) { } // CreateIssueComment create a comment for an issue -func CreateIssueComment(ctx *context.APIContext, form api.CreateIssueCommentOption) { +func CreateIssueComment(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/issues/{index}/comments issue issueCreateComment // --- // summary: Add a comment to an issue @@ -208,7 +209,7 @@ func CreateIssueComment(ctx *context.APIContext, form api.CreateIssueCommentOpti // "$ref": "#/responses/Comment" // "403": // "$ref": "#/responses/forbidden" - + form := web.GetForm(ctx).(*api.CreateIssueCommentOption) issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err) @@ -298,7 +299,7 @@ func GetIssueComment(ctx *context.APIContext) { } // EditIssueComment modify a comment of an issue -func EditIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption) { +func EditIssueComment(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/issues/comments/{id} issue issueEditComment // --- // summary: Edit a comment @@ -337,11 +338,12 @@ func EditIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption) // "404": // "$ref": "#/responses/notFound" - editIssueComment(ctx, form) + form := web.GetForm(ctx).(*api.EditIssueCommentOption) + editIssueComment(ctx, *form) } // EditIssueCommentDeprecated modify a comment of an issue -func EditIssueCommentDeprecated(ctx *context.APIContext, form api.EditIssueCommentOption) { +func EditIssueCommentDeprecated(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/issues/{index}/comments/{id} issue issueEditCommentDeprecated // --- // summary: Edit a comment @@ -386,7 +388,8 @@ func EditIssueCommentDeprecated(ctx *context.APIContext, form api.EditIssueComme // "404": // "$ref": "#/responses/notFound" - editIssueComment(ctx, form) + form := web.GetForm(ctx).(*api.EditIssueCommentOption) + editIssueComment(ctx, *form) } func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption) { diff --git a/routers/api/v1/repo/issue_label.go b/routers/api/v1/repo/issue_label.go index 8b2a1988fa..d7f64b2d99 100644 --- a/routers/api/v1/repo/issue_label.go +++ b/routers/api/v1/repo/issue_label.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" issue_service "code.gitea.io/gitea/services/issue" ) @@ -64,7 +65,7 @@ func ListIssueLabels(ctx *context.APIContext) { } // AddIssueLabels add labels for an issue -func AddIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) { +func AddIssueLabels(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/issues/{index}/labels issue issueAddLabel // --- // summary: Add a label to an issue @@ -99,7 +100,8 @@ func AddIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) { // "403": // "$ref": "#/responses/forbidden" - issue, labels, err := prepareForReplaceOrAdd(ctx, form) + form := web.GetForm(ctx).(*api.IssueLabelsOption) + issue, labels, err := prepareForReplaceOrAdd(ctx, *form) if err != nil { return } @@ -190,7 +192,7 @@ func DeleteIssueLabel(ctx *context.APIContext) { } // ReplaceIssueLabels replace labels for an issue -func ReplaceIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) { +func ReplaceIssueLabels(ctx *context.APIContext) { // swagger:operation PUT /repos/{owner}/{repo}/issues/{index}/labels issue issueReplaceLabels // --- // summary: Replace an issue's labels @@ -224,8 +226,8 @@ func ReplaceIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) { // "$ref": "#/responses/LabelList" // "403": // "$ref": "#/responses/forbidden" - - issue, labels, err := prepareForReplaceOrAdd(ctx, form) + form := web.GetForm(ctx).(*api.IssueLabelsOption) + issue, labels, err := prepareForReplaceOrAdd(ctx, *form) if err != nil { return } diff --git a/routers/api/v1/repo/issue_reaction.go b/routers/api/v1/repo/issue_reaction.go index dfe618480f..3994c6b941 100644 --- a/routers/api/v1/repo/issue_reaction.go +++ b/routers/api/v1/repo/issue_reaction.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -90,7 +91,7 @@ func GetIssueCommentReactions(ctx *context.APIContext) { } // PostIssueCommentReaction add a reaction to a comment of an issue -func PostIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOption) { +func PostIssueCommentReaction(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/issues/comments/{id}/reactions issue issuePostCommentReaction // --- // summary: Add a reaction to a comment of an issue @@ -127,11 +128,13 @@ func PostIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOpti // "403": // "$ref": "#/responses/forbidden" - changeIssueCommentReaction(ctx, form, true) + form := web.GetForm(ctx).(*api.EditReactionOption) + + changeIssueCommentReaction(ctx, *form, true) } // DeleteIssueCommentReaction remove a reaction from a comment of an issue -func DeleteIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOption) { +func DeleteIssueCommentReaction(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo}/issues/comments/{id}/reactions issue issueDeleteCommentReaction // --- // summary: Remove a reaction from a comment of an issue @@ -166,7 +169,9 @@ func DeleteIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOp // "403": // "$ref": "#/responses/forbidden" - changeIssueCommentReaction(ctx, form, false) + form := web.GetForm(ctx).(*api.EditReactionOption) + + changeIssueCommentReaction(ctx, *form, false) } func changeIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOption, isCreateType bool) { @@ -304,7 +309,7 @@ func GetIssueReactions(ctx *context.APIContext) { } // PostIssueReaction add a reaction to an issue -func PostIssueReaction(ctx *context.APIContext, form api.EditReactionOption) { +func PostIssueReaction(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/issues/{index}/reactions issue issuePostIssueReaction // --- // summary: Add a reaction to an issue @@ -340,12 +345,12 @@ func PostIssueReaction(ctx *context.APIContext, form api.EditReactionOption) { // "$ref": "#/responses/Reaction" // "403": // "$ref": "#/responses/forbidden" - - changeIssueReaction(ctx, form, true) + form := web.GetForm(ctx).(*api.EditReactionOption) + changeIssueReaction(ctx, *form, true) } // DeleteIssueReaction remove a reaction from an issue -func DeleteIssueReaction(ctx *context.APIContext, form api.EditReactionOption) { +func DeleteIssueReaction(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/reactions issue issueDeleteIssueReaction // --- // summary: Remove a reaction from an issue @@ -379,8 +384,8 @@ func DeleteIssueReaction(ctx *context.APIContext, form api.EditReactionOption) { // "$ref": "#/responses/empty" // "403": // "$ref": "#/responses/forbidden" - - changeIssueReaction(ctx, form, false) + form := web.GetForm(ctx).(*api.EditReactionOption) + changeIssueReaction(ctx, *form, false) } func changeIssueReaction(ctx *context.APIContext, form api.EditReactionOption, isCreateType bool) { diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go index 70ce420b77..642704800b 100644 --- a/routers/api/v1/repo/issue_tracked_time.go +++ b/routers/api/v1/repo/issue_tracked_time.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -132,7 +133,7 @@ func ListTrackedTimes(ctx *context.APIContext) { } // AddTime add time manual to the given issue -func AddTime(ctx *context.APIContext, form api.AddTimeOption) { +func AddTime(ctx *context.APIContext) { // swagger:operation Post /repos/{owner}/{repo}/issues/{index}/times issue issueAddTime // --- // summary: Add tracked time to a issue @@ -168,7 +169,7 @@ func AddTime(ctx *context.APIContext, form api.AddTimeOption) { // "$ref": "#/responses/error" // "403": // "$ref": "#/responses/forbidden" - + form := web.GetForm(ctx).(*api.AddTimeOption) issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrIssueNotExist(err) { diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go index 3e6174f621..b1b465ca11 100644 --- a/routers/api/v1/repo/key.go +++ b/routers/api/v1/repo/key.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -185,7 +186,7 @@ func HandleAddKeyError(ctx *context.APIContext, err error) { } // CreateDeployKey create deploy key for a repository -func CreateDeployKey(ctx *context.APIContext, form api.CreateKeyOption) { +func CreateDeployKey(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/keys repository repoCreateKey // --- // summary: Add a key to a repository @@ -214,6 +215,7 @@ func CreateDeployKey(ctx *context.APIContext, form api.CreateKeyOption) { // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.CreateKeyOption) content, err := models.CheckPublicKeyString(form.Key) if err != nil { HandleCheckKeyStringError(ctx, err) diff --git a/routers/api/v1/repo/label.go b/routers/api/v1/repo/label.go index fef6ebf07a..f71683f6ec 100644 --- a/routers/api/v1/repo/label.go +++ b/routers/api/v1/repo/label.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -108,7 +109,7 @@ func GetLabel(ctx *context.APIContext) { } // CreateLabel create a label for a repository -func CreateLabel(ctx *context.APIContext, form api.CreateLabelOption) { +func CreateLabel(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/labels issue issueCreateLabel // --- // summary: Create a label @@ -137,6 +138,7 @@ func CreateLabel(ctx *context.APIContext, form api.CreateLabelOption) { // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.CreateLabelOption) form.Color = strings.Trim(form.Color, " ") if len(form.Color) == 6 { form.Color = "#" + form.Color @@ -160,7 +162,7 @@ func CreateLabel(ctx *context.APIContext, form api.CreateLabelOption) { } // EditLabel modify a label for a repository -func EditLabel(ctx *context.APIContext, form api.EditLabelOption) { +func EditLabel(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/labels/{id} issue issueEditLabel // --- // summary: Update a label @@ -195,6 +197,7 @@ func EditLabel(ctx *context.APIContext, form api.EditLabelOption) { // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.EditLabelOption) label, err := models.GetLabelInRepoByID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")) if err != nil { if models.IsErrRepoLabelNotExist(err) { diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go index 0b829c9dfb..61cd12b991 100644 --- a/routers/api/v1/repo/migrate.go +++ b/routers/api/v1/repo/migrate.go @@ -12,9 +12,9 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/migrations" @@ -24,10 +24,11 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" ) // Migrate migrate remote git repository to gitea -func Migrate(ctx *context.APIContext, form api.MigrateRepoOptions) { +func Migrate(ctx *context.APIContext) { // swagger:operation POST /repos/migrate repository repoMigrate // --- // summary: Migrate a remote git repository @@ -48,6 +49,8 @@ func Migrate(ctx *context.APIContext, form api.MigrateRepoOptions) { // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.MigrateRepoOptions) + //get repoOwner var ( repoOwner *models.User diff --git a/routers/api/v1/repo/milestone.go b/routers/api/v1/repo/milestone.go index db86d0868f..fc8b4efdbb 100644 --- a/routers/api/v1/repo/milestone.go +++ b/routers/api/v1/repo/milestone.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -110,7 +111,7 @@ func GetMilestone(ctx *context.APIContext) { } // CreateMilestone create a milestone for a repository -func CreateMilestone(ctx *context.APIContext, form api.CreateMilestoneOption) { +func CreateMilestone(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/milestones issue issueCreateMilestone // --- // summary: Create a milestone @@ -136,6 +137,7 @@ func CreateMilestone(ctx *context.APIContext, form api.CreateMilestoneOption) { // responses: // "201": // "$ref": "#/responses/Milestone" + form := web.GetForm(ctx).(*api.CreateMilestoneOption) if form.Deadline == nil { defaultDeadline, _ := time.ParseInLocation("2006-01-02", "9999-12-31", time.Local) @@ -162,7 +164,7 @@ func CreateMilestone(ctx *context.APIContext, form api.CreateMilestoneOption) { } // EditMilestone modify a milestone for a repository by ID and if not available by name -func EditMilestone(ctx *context.APIContext, form api.EditMilestoneOption) { +func EditMilestone(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/milestones/{id} issue issueEditMilestone // --- // summary: Update a milestone @@ -193,7 +195,7 @@ func EditMilestone(ctx *context.APIContext, form api.EditMilestoneOption) { // responses: // "200": // "$ref": "#/responses/Milestone" - + form := web.GetForm(ctx).(*api.EditMilestoneOption) milestone := getMilestoneByIDOrName(ctx) if ctx.Written() { return diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index b2b71180a4..38dac36553 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -11,21 +11,22 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" issue_service "code.gitea.io/gitea/services/issue" pull_service "code.gitea.io/gitea/services/pull" ) // ListPullRequests returns a list of all PRs -func ListPullRequests(ctx *context.APIContext, form api.ListPullRequestsOptions) { +func ListPullRequests(ctx *context.APIContext) { // swagger:operation GET /repos/{owner}/{repo}/pulls repository repoListPullRequests // --- // summary: List a repo's pull requests @@ -253,7 +254,7 @@ func DownloadPullDiffOrPatch(ctx *context.APIContext, patch bool) { } // CreatePullRequest does what it says -func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption) { +func CreatePullRequest(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/pulls repository repoCreatePullRequest // --- // summary: Create a pull request @@ -284,6 +285,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption // "422": // "$ref": "#/responses/validationError" + form := *web.GetForm(ctx).(*api.CreatePullRequestOption) if form.Head == form.Base { ctx.Error(http.StatusUnprocessableEntity, "BaseHeadSame", "Invalid PullRequest: There are no changes between the head and the base") @@ -437,7 +439,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption } // EditPullRequest does what it says -func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) { +func EditPullRequest(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/pulls/{index} repository repoEditPullRequest // --- // summary: Update a pull request. If using deadline only the date will be taken into account, and time of day ignored. @@ -478,6 +480,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) { // "422": // "$ref": "#/responses/validationError" + form := web.GetForm(ctx).(*api.EditPullRequestOption) pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrPullRequestNotExist(err) { @@ -685,7 +688,7 @@ func IsPullRequestMerged(ctx *context.APIContext) { } // MergePullRequest merges a PR given an index -func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) { +func MergePullRequest(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/merge repository repoMergePullRequest // --- // summary: Merge a pull request @@ -720,6 +723,7 @@ func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) { // "409": // "$ref": "#/responses/error" + form := web.GetForm(ctx).(*auth.MergePullRequestForm) pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrPullRequestNotExist(err) { diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index 9e7fd15664..d39db4c660 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/git" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" issue_service "code.gitea.io/gitea/services/issue" pull_service "code.gitea.io/gitea/services/pull" @@ -258,7 +259,7 @@ func DeletePullReview(ctx *context.APIContext) { } // CreatePullReview create a review to an pull request -func CreatePullReview(ctx *context.APIContext, opts api.CreatePullReviewOptions) { +func CreatePullReview(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/reviews repository repoCreatePullReview // --- // summary: Create a review to an pull request @@ -294,6 +295,7 @@ func CreatePullReview(ctx *context.APIContext, opts api.CreatePullReviewOptions) // "422": // "$ref": "#/responses/validationError" + opts := web.GetForm(ctx).(*api.CreatePullReviewOptions) pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrPullRequestNotExist(err) { @@ -373,7 +375,7 @@ func CreatePullReview(ctx *context.APIContext, opts api.CreatePullReviewOptions) } // SubmitPullReview submit a pending review to an pull request -func SubmitPullReview(ctx *context.APIContext, opts api.SubmitPullReviewOptions) { +func SubmitPullReview(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/reviews/{id} repository repoSubmitPullReview // --- // summary: Submit a pending review to an pull request @@ -415,6 +417,7 @@ func SubmitPullReview(ctx *context.APIContext, opts api.SubmitPullReviewOptions) // "422": // "$ref": "#/responses/validationError" + opts := web.GetForm(ctx).(*api.SubmitPullReviewOptions) review, pr, isWrong := prepareSingleReview(ctx) if isWrong { return @@ -542,7 +545,7 @@ func prepareSingleReview(ctx *context.APIContext) (*models.Review, *models.PullR } // CreateReviewRequests create review requests to an pull request -func CreateReviewRequests(ctx *context.APIContext, opts api.PullReviewRequestOptions) { +func CreateReviewRequests(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/requested_reviewers repository repoCreatePullReviewRequests // --- // summary: create review requests for a pull request @@ -577,11 +580,13 @@ func CreateReviewRequests(ctx *context.APIContext, opts api.PullReviewRequestOpt // "$ref": "#/responses/validationError" // "404": // "$ref": "#/responses/notFound" - apiReviewRequest(ctx, opts, true) + + opts := web.GetForm(ctx).(*api.PullReviewRequestOptions) + apiReviewRequest(ctx, *opts, true) } // DeleteReviewRequests delete review requests to an pull request -func DeleteReviewRequests(ctx *context.APIContext, opts api.PullReviewRequestOptions) { +func DeleteReviewRequests(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo}/pulls/{index}/requested_reviewers repository repoDeletePullReviewRequests // --- // summary: cancel review requests for a pull request @@ -616,7 +621,8 @@ func DeleteReviewRequests(ctx *context.APIContext, opts api.PullReviewRequestOpt // "$ref": "#/responses/validationError" // "404": // "$ref": "#/responses/notFound" - apiReviewRequest(ctx, opts, false) + opts := web.GetForm(ctx).(*api.PullReviewRequestOptions) + apiReviewRequest(ctx, *opts, false) } func apiReviewRequest(ctx *context.APIContext, opts api.PullReviewRequestOptions, isAdd bool) { diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index 358cc01143..08d92e6c0a 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" releaseservice "code.gitea.io/gitea/services/release" ) @@ -124,7 +125,7 @@ func ListReleases(ctx *context.APIContext) { } // CreateRelease create a release -func CreateRelease(ctx *context.APIContext, form api.CreateReleaseOption) { +func CreateRelease(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/releases repository repoCreateRelease // --- // summary: Create a release @@ -154,7 +155,7 @@ func CreateRelease(ctx *context.APIContext, form api.CreateReleaseOption) { // "$ref": "#/responses/notFound" // "409": // "$ref": "#/responses/error" - + form := web.GetForm(ctx).(*api.CreateReleaseOption) rel, err := models.GetRelease(ctx.Repo.Repository.ID, form.TagName) if err != nil { if !models.IsErrReleaseNotExist(err) { @@ -210,7 +211,7 @@ func CreateRelease(ctx *context.APIContext, form api.CreateReleaseOption) { } // EditRelease edit a release -func EditRelease(ctx *context.APIContext, form api.EditReleaseOption) { +func EditRelease(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/releases/{id} repository repoEditRelease // --- // summary: Update a release @@ -245,6 +246,7 @@ func EditRelease(ctx *context.APIContext, form api.EditReleaseOption) { // "404": // "$ref": "#/responses/notFound" + form := web.GetForm(ctx).(*api.EditReleaseOption) id := ctx.ParamsInt64(":id") rel, err := models.GetReleaseByID(id) if err != nil && !models.IsErrReleaseNotExist(err) { diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go index 51e1b160da..0a6425cddc 100644 --- a/routers/api/v1/repo/release_attachment.go +++ b/routers/api/v1/repo/release_attachment.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/upload" + "code.gitea.io/gitea/modules/web" ) // GetReleaseAttachment gets a single attachment of the release @@ -168,7 +169,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) { } // Get uploaded file from request - file, header, err := ctx.GetFile("attachment") + file, header, err := ctx.Req.FormFile("attachment") if err != nil { ctx.Error(http.StatusInternalServerError, "GetFile", err) return @@ -208,7 +209,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) { } // EditReleaseAttachment updates the given attachment -func EditReleaseAttachment(ctx *context.APIContext, form api.EditAttachmentOptions) { +func EditReleaseAttachment(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo}/releases/{id}/assets/{attachment_id} repository repoEditReleaseAttachment // --- // summary: Edit a release attachment @@ -247,6 +248,8 @@ func EditReleaseAttachment(ctx *context.APIContext, form api.EditAttachmentOptio // "201": // "$ref": "#/responses/Attachment" + form := web.GetForm(ctx).(*api.EditAttachmentOptions) + // Check if release exists an load release releaseID := ctx.ParamsInt64(":id") attachID := ctx.ParamsInt64(":asset") diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 82d380a814..375f2418b9 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -20,6 +20,7 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/validation" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" repo_service "code.gitea.io/gitea/services/repository" ) @@ -277,7 +278,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateR } // Create one repository of mine -func Create(ctx *context.APIContext, opt api.CreateRepoOption) { +func Create(ctx *context.APIContext) { // swagger:operation POST /user/repos repository user createCurrentUserRepo // --- // summary: Create a repository @@ -297,17 +298,17 @@ func Create(ctx *context.APIContext, opt api.CreateRepoOption) { // description: The repository with the same name already exists. // "422": // "$ref": "#/responses/validationError" - + opt := web.GetForm(ctx).(*api.CreateRepoOption) if ctx.User.IsOrganization() { // Shouldn't reach this condition, but just in case. ctx.Error(http.StatusUnprocessableEntity, "", "not allowed creating repository for organization") return } - CreateUserRepo(ctx, ctx.User, opt) + CreateUserRepo(ctx, ctx.User, *opt) } // CreateOrgRepoDeprecated create one repository of the organization -func CreateOrgRepoDeprecated(ctx *context.APIContext, opt api.CreateRepoOption) { +func CreateOrgRepoDeprecated(ctx *context.APIContext) { // swagger:operation POST /org/{org}/repos organization createOrgRepoDeprecated // --- // summary: Create a repository in an organization @@ -334,11 +335,11 @@ func CreateOrgRepoDeprecated(ctx *context.APIContext, opt api.CreateRepoOption) // "403": // "$ref": "#/responses/forbidden" - CreateOrgRepo(ctx, opt) + CreateOrgRepo(ctx) } // CreateOrgRepo create one repository of the organization -func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) { +func CreateOrgRepo(ctx *context.APIContext) { // swagger:operation POST /orgs/{org}/repos organization createOrgRepo // --- // summary: Create a repository in an organization @@ -363,7 +364,7 @@ func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) { // "$ref": "#/responses/notFound" // "403": // "$ref": "#/responses/forbidden" - + opt := web.GetForm(ctx).(*api.CreateRepoOption) org, err := models.GetOrgByName(ctx.Params(":org")) if err != nil { if models.IsErrOrgNotExist(err) { @@ -389,7 +390,7 @@ func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) { return } } - CreateUserRepo(ctx, org, opt) + CreateUserRepo(ctx, org, *opt) } // Get one repository @@ -457,7 +458,7 @@ func GetByID(ctx *context.APIContext) { } // Edit edit repository properties -func Edit(ctx *context.APIContext, opts api.EditRepoOption) { +func Edit(ctx *context.APIContext) { // swagger:operation PATCH /repos/{owner}/{repo} repository repoEdit // --- // summary: Edit a repository's properties. Only fields that are set will be changed. @@ -488,6 +489,8 @@ func Edit(ctx *context.APIContext, opts api.EditRepoOption) { // "422": // "$ref": "#/responses/validationError" + opts := *web.GetForm(ctx).(*api.EditRepoOption) + if err := updateBasicProperties(ctx, opts); err != nil { return } diff --git a/routers/api/v1/repo/repo_test.go b/routers/api/v1/repo/repo_test.go index 053134ec61..a1bd3e85d7 100644 --- a/routers/api/v1/repo/repo_test.go +++ b/routers/api/v1/repo/repo_test.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/modules/web" "github.com/stretchr/testify/assert" ) @@ -53,7 +54,9 @@ func TestRepoEdit(t *testing.T) { Archived: &archived, } - Edit(&context.APIContext{Context: ctx, Org: nil}, opts) + var apiCtx = &context.APIContext{Context: ctx, Org: nil} + web.SetForm(apiCtx, &opts) + Edit(apiCtx) assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) models.AssertExistsAndLoadBean(t, &models.Repository{ @@ -73,7 +76,9 @@ func TestRepoEditNameChange(t *testing.T) { Name: &name, } - Edit(&context.APIContext{Context: ctx, Org: nil}, opts) + var apiCtx = &context.APIContext{Context: ctx, Org: nil} + web.SetForm(apiCtx, &opts) + Edit(apiCtx) assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) models.AssertExistsAndLoadBean(t, &models.Repository{ diff --git a/routers/api/v1/repo/status.go b/routers/api/v1/repo/status.go index 9c0d902f76..7ab399b572 100644 --- a/routers/api/v1/repo/status.go +++ b/routers/api/v1/repo/status.go @@ -13,11 +13,12 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/repofiles" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) // NewCommitStatus creates a new CommitStatus -func NewCommitStatus(ctx *context.APIContext, form api.CreateStatusOption) { +func NewCommitStatus(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/statuses/{sha} repository repoCreateStatus // --- // summary: Create a commit status @@ -49,6 +50,7 @@ func NewCommitStatus(ctx *context.APIContext, form api.CreateStatusOption) { // "400": // "$ref": "#/responses/error" + form := web.GetForm(ctx).(*api.CreateStatusOption) sha := ctx.Params("sha") if len(sha) == 0 { ctx.Error(http.StatusBadRequest, "sha not given", nil) diff --git a/routers/api/v1/repo/topic.go b/routers/api/v1/repo/topic.go index 41fa37a2c1..c612c2942c 100644 --- a/routers/api/v1/repo/topic.go +++ b/routers/api/v1/repo/topic.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -66,7 +67,7 @@ func ListTopics(ctx *context.APIContext) { } // UpdateTopics updates repo with a new set of topics -func UpdateTopics(ctx *context.APIContext, form api.RepoTopicOptions) { +func UpdateTopics(ctx *context.APIContext) { // swagger:operation PUT /repos/{owner}/{repo}/topics repository repoUpdateTopics // --- // summary: Replace list of topics for a repository @@ -93,6 +94,7 @@ func UpdateTopics(ctx *context.APIContext, form api.RepoTopicOptions) { // "422": // "$ref": "#/responses/invalidTopicsError" + form := web.GetForm(ctx).(*api.RepoTopicOptions) topicNames := form.Topics validTopics, invalidTopics := models.SanitizeAndValidateTopics(topicNames) diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go index a0999a6ce2..656ace032e 100644 --- a/routers/api/v1/repo/transfer.go +++ b/routers/api/v1/repo/transfer.go @@ -14,11 +14,12 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" repo_service "code.gitea.io/gitea/services/repository" ) // Transfer transfers the ownership of a repository -func Transfer(ctx *context.APIContext, opts api.TransferRepoOption) { +func Transfer(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/transfer repository repoTransfer // --- // summary: Transfer a repo ownership @@ -51,6 +52,8 @@ func Transfer(ctx *context.APIContext, opts api.TransferRepoOption) { // "422": // "$ref": "#/responses/validationError" + opts := web.GetForm(ctx).(*api.TransferRepoOption) + newOwner, err := models.GetUserByName(opts.NewOwner) if err != nil { if models.IsErrUserNotExist(err) { diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index a3bb9cc657..8919a969ec 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -5,7 +5,7 @@ package swagger import ( - "code.gitea.io/gitea/modules/auth" + auth "code.gitea.io/gitea/modules/forms" api "code.gitea.io/gitea/modules/structs" ) diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index 547730ea57..33b27d60e0 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -61,7 +62,7 @@ func ListAccessTokens(ctx *context.APIContext) { } // CreateAccessToken create access tokens -func CreateAccessToken(ctx *context.APIContext, form api.CreateAccessTokenOption) { +func CreateAccessToken(ctx *context.APIContext) { // swagger:operation POST /users/{username}/tokens user userCreateToken // --- // summary: Create an access token @@ -88,6 +89,8 @@ func CreateAccessToken(ctx *context.APIContext, form api.CreateAccessTokenOption // "201": // "$ref": "#/responses/AccessToken" + form := web.GetForm(ctx).(*api.CreateAccessTokenOption) + t := &models.AccessToken{ UID: ctx.User.ID, Name: form.Name, @@ -181,7 +184,7 @@ func DeleteAccessToken(ctx *context.APIContext) { } // CreateOauth2Application is the handler to create a new OAuth2 Application for the authenticated user -func CreateOauth2Application(ctx *context.APIContext, data api.CreateOAuth2ApplicationOptions) { +func CreateOauth2Application(ctx *context.APIContext) { // swagger:operation POST /user/applications/oauth2 user userCreateOAuth2Application // --- // summary: creates a new OAuth2 application @@ -196,6 +199,9 @@ func CreateOauth2Application(ctx *context.APIContext, data api.CreateOAuth2Appli // responses: // "201": // "$ref": "#/responses/OAuth2Application" + + data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions) + app, err := models.CreateOAuth2Application(models.CreateOAuth2ApplicationOptions{ Name: data.Name, UserID: ctx.User.ID, @@ -309,7 +315,7 @@ func GetOauth2Application(ctx *context.APIContext) { } // UpdateOauth2Application update OAuth2 Application -func UpdateOauth2Application(ctx *context.APIContext, data api.CreateOAuth2ApplicationOptions) { +func UpdateOauth2Application(ctx *context.APIContext) { // swagger:operation PATCH /user/applications/oauth2/{id} user userUpdateOAuth2Application // --- // summary: update an OAuth2 Application, this includes regenerating the client secret @@ -332,6 +338,8 @@ func UpdateOauth2Application(ctx *context.APIContext, data api.CreateOAuth2Appli // "$ref": "#/responses/OAuth2Application" appID := ctx.ParamsInt64(":id") + data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions) + app, err := models.UpdateOAuth2Application(models.UpdateOAuth2ApplicationOptions{ Name: data.Name, UserID: ctx.User.ID, diff --git a/routers/api/v1/user/email.go b/routers/api/v1/user/email.go index d848f5e58d..bc8b2fa87b 100644 --- a/routers/api/v1/user/email.go +++ b/routers/api/v1/user/email.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" ) // ListEmails list all of the authenticated user's email addresses @@ -40,7 +41,7 @@ func ListEmails(ctx *context.APIContext) { } // AddEmail add an email address -func AddEmail(ctx *context.APIContext, form api.CreateEmailOption) { +func AddEmail(ctx *context.APIContext) { // swagger:operation POST /user/emails user userAddEmail // --- // summary: Add email addresses @@ -61,7 +62,7 @@ func AddEmail(ctx *context.APIContext, form api.CreateEmailOption) { // "$ref": "#/responses/EmailList" // "422": // "$ref": "#/responses/validationError" - + form := web.GetForm(ctx).(*api.CreateEmailOption) if len(form.Emails) == 0 { ctx.Error(http.StatusUnprocessableEntity, "", "Email list empty") return @@ -96,7 +97,7 @@ func AddEmail(ctx *context.APIContext, form api.CreateEmailOption) { } // DeleteEmail delete email -func DeleteEmail(ctx *context.APIContext, form api.DeleteEmailOption) { +func DeleteEmail(ctx *context.APIContext) { // swagger:operation DELETE /user/emails user userDeleteEmail // --- // summary: Delete email addresses @@ -110,7 +111,7 @@ func DeleteEmail(ctx *context.APIContext, form api.DeleteEmailOption) { // responses: // "204": // "$ref": "#/responses/empty" - + form := web.GetForm(ctx).(*api.DeleteEmailOption) if len(form.Emails) == 0 { ctx.Status(http.StatusNoContent) return diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go index 7290ef485a..51bcaeacc6 100644 --- a/routers/api/v1/user/gpg_key.go +++ b/routers/api/v1/user/gpg_key.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -133,7 +134,7 @@ type swaggerUserCurrentPostGPGKey struct { } //CreateGPGKey create a GPG key belonging to the authenticated user -func CreateGPGKey(ctx *context.APIContext, form api.CreateGPGKeyOption) { +func CreateGPGKey(ctx *context.APIContext) { // swagger:operation POST /user/gpg_keys user userCurrentPostGPGKey // --- // summary: Create a GPG key @@ -149,7 +150,8 @@ func CreateGPGKey(ctx *context.APIContext, form api.CreateGPGKeyOption) { // "422": // "$ref": "#/responses/validationError" - CreateUserGPGKey(ctx, form, ctx.User.ID) + form := web.GetForm(ctx).(*api.CreateGPGKeyOption) + CreateUserGPGKey(ctx, *form, ctx.User.ID) } //DeleteGPGKey remove a GPG key belonging to the authenticated user diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index fa16df1836..df8a11c61f 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/repo" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -204,7 +205,7 @@ func CreateUserPublicKey(ctx *context.APIContext, form api.CreateKeyOption, uid } // CreatePublicKey create one public key for me -func CreatePublicKey(ctx *context.APIContext, form api.CreateKeyOption) { +func CreatePublicKey(ctx *context.APIContext) { // swagger:operation POST /user/keys user userCurrentPostKey // --- // summary: Create a public key @@ -223,7 +224,8 @@ func CreatePublicKey(ctx *context.APIContext, form api.CreateKeyOption) { // "422": // "$ref": "#/responses/validationError" - CreateUserPublicKey(ctx, form, ctx.User.ID) + form := web.GetForm(ctx).(*api.CreateKeyOption) + CreateUserPublicKey(ctx, *form, ctx.User.ID) } // DeletePublicKey delete one public key diff --git a/routers/init.go b/routers/init.go index 79e2f9130a..9d13bc9ed5 100644 --- a/routers/init.go +++ b/routers/init.go @@ -37,22 +37,16 @@ import ( pull_service "code.gitea.io/gitea/services/pull" "code.gitea.io/gitea/services/repository" "code.gitea.io/gitea/services/webhook" - - "gitea.com/macaron/macaron" ) func checkRunMode() { switch setting.RunMode { - case "dev": - git.Debug = true - case "test": + case "dev", "test": git.Debug = true default: - macaron.Env = macaron.PROD - macaron.ColorLog = false git.Debug = false } - log.Info("Run Mode: %s", strings.Title(macaron.Env)) + log.Info("Run Mode: %s", strings.Title(setting.RunMode)) } // NewServices init new services diff --git a/routers/install.go b/routers/install.go index 50e929b6f3..5dcd1d48a3 100644 --- a/routers/install.go +++ b/routers/install.go @@ -11,18 +11,23 @@ import ( "os/exec" "path/filepath" "strings" + "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/generate" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/middlewares" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/user" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" + "gitea.com/go-chi/session" "gopkg.in/ini.v1" ) @@ -33,17 +38,39 @@ const ( ) // InstallInit prepare for rendering installation page -func InstallInit(ctx *context.Context) { - if setting.InstallLock { - ctx.Header().Add("Refresh", "1; url="+setting.AppURL+"user/login") - ctx.HTML(200, tplPostInstall) - return - } - - ctx.Data["Title"] = ctx.Tr("install.install") - ctx.Data["PageIsInstall"] = true +func InstallInit(next http.Handler) http.Handler { + var rnd = templates.HTMLRenderer() - ctx.Data["DbOptions"] = setting.SupportedDatabases + return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + if setting.InstallLock { + resp.Header().Add("Refresh", "1; url="+setting.AppURL+"user/login") + _ = rnd.HTML(resp, 200, string(tplPostInstall), nil) + return + } + var locale = middlewares.Locale(resp, req) + var startTime = time.Now() + var ctx = context.Context{ + Resp: context.NewResponse(resp), + Flash: &middlewares.Flash{}, + Locale: locale, + Render: rnd, + Session: session.GetSession(req), + Data: map[string]interface{}{ + "Title": locale.Tr("install.install"), + "PageIsInstall": true, + "DbOptions": setting.SupportedDatabases, + "i18n": locale, + "Language": locale.Language(), + "CurrentURL": setting.AppSubURL + req.URL.RequestURI(), + "PageStartTime": startTime, + "TmplLoadTimes": func() string { + return time.Since(startTime).String() + }, + }, + } + ctx.Req = context.WithContext(req, &ctx) + next.ServeHTTP(resp, ctx.Req) + }) } // Install render installation page @@ -116,12 +143,13 @@ func Install(ctx *context.Context) { form.DefaultEnableTimetracking = setting.Service.DefaultEnableTimetracking form.NoReplyAddress = setting.Service.NoReplyAddress - auth.AssignForm(form, ctx.Data) + middlewares.AssignForm(form, ctx.Data) ctx.HTML(200, tplInstall) } // InstallPost response for submit install items -func InstallPost(ctx *context.Context, form auth.InstallForm) { +func InstallPost(ctx *context.Context) { + form := *web.GetForm(ctx).(*auth.InstallForm) var err error ctx.Data["CurDbOption"] = form.DbType diff --git a/routers/org/org.go b/routers/org/org.go index 85bc25217b..98a327a97e 100644 --- a/routers/org/org.go +++ b/routers/org/org.go @@ -9,11 +9,12 @@ import ( "errors" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" ) const ( @@ -33,7 +34,8 @@ func Create(ctx *context.Context) { } // CreatePost response for create organization -func CreatePost(ctx *context.Context, form auth.CreateOrgForm) { +func CreatePost(ctx *context.Context) { + form := *web.GetForm(ctx).(*auth.CreateOrgForm) ctx.Data["Title"] = ctx.Tr("new_org") if !ctx.User.CanCreateOrganization() { diff --git a/routers/org/org_labels.go b/routers/org/org_labels.go index e5b9d9ddee..554f86c964 100644 --- a/routers/org/org_labels.go +++ b/routers/org/org_labels.go @@ -6,8 +6,9 @@ package org import ( "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" + "code.gitea.io/gitea/modules/web" ) // RetrieveLabels find all the labels of an organization @@ -26,7 +27,8 @@ func RetrieveLabels(ctx *context.Context) { } // NewLabel create new label for organization -func NewLabel(ctx *context.Context, form auth.CreateLabelForm) { +func NewLabel(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateLabelForm) ctx.Data["Title"] = ctx.Tr("repo.labels") ctx.Data["PageIsLabels"] = true @@ -50,7 +52,8 @@ func NewLabel(ctx *context.Context, form auth.CreateLabelForm) { } // UpdateLabel update a label's name and color -func UpdateLabel(ctx *context.Context, form auth.CreateLabelForm) { +func UpdateLabel(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateLabelForm) l, err := models.GetLabelInOrgByID(ctx.Org.Organization.ID, form.ID) if err != nil { switch { @@ -86,7 +89,8 @@ func DeleteLabel(ctx *context.Context) { } // InitializeLabels init labels for an organization -func InitializeLabels(ctx *context.Context, form auth.InitializeLabelsForm) { +func InitializeLabels(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.InitializeLabelsForm) if ctx.HasError() { ctx.Redirect(ctx.Repo.RepoLink + "/labels") return diff --git a/routers/org/setting.go b/routers/org/setting.go index 05075ca820..ac12066258 100644 --- a/routers/org/setting.go +++ b/routers/org/setting.go @@ -9,11 +9,12 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" userSetting "code.gitea.io/gitea/routers/user/setting" ) @@ -38,7 +39,8 @@ func Settings(ctx *context.Context) { } // SettingsPost response for settings change submited -func SettingsPost(ctx *context.Context, form auth.UpdateOrgSettingForm) { +func SettingsPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.UpdateOrgSettingForm) ctx.Data["Title"] = ctx.Tr("org.settings") ctx.Data["PageIsSettingsOptions"] = true ctx.Data["CurrentVisibility"] = ctx.Org.Organization.Visibility @@ -115,7 +117,8 @@ func SettingsPost(ctx *context.Context, form auth.UpdateOrgSettingForm) { } // SettingsAvatar response for change avatar on settings page -func SettingsAvatar(ctx *context.Context, form auth.AvatarForm) { +func SettingsAvatar(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AvatarForm) form.Source = auth.AvatarLocal if err := userSetting.UpdateAvatarSetting(ctx, form, ctx.Org.Organization); err != nil { ctx.Flash.Error(err.Error()) diff --git a/routers/org/teams.go b/routers/org/teams.go index fa98add601..cfa49d4e97 100644 --- a/routers/org/teams.go +++ b/routers/org/teams.go @@ -11,10 +11,11 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" ) @@ -186,7 +187,8 @@ func NewTeam(ctx *context.Context) { } // NewTeamPost response for create new team -func NewTeamPost(ctx *context.Context, form auth.CreateTeamForm) { +func NewTeamPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateTeamForm) ctx.Data["Title"] = ctx.Org.Organization.FullName ctx.Data["PageIsOrgTeams"] = true ctx.Data["PageIsOrgTeamsNew"] = true @@ -274,7 +276,8 @@ func EditTeam(ctx *context.Context) { } // EditTeamPost response for modify team information -func EditTeamPost(ctx *context.Context, form auth.CreateTeamForm) { +func EditTeamPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateTeamForm) t := ctx.Org.Team ctx.Data["Title"] = ctx.Org.Organization.FullName ctx.Data["PageIsOrgTeams"] = true diff --git a/routers/private/hook.go b/routers/private/hook.go index 34e849f6f7..853d3069ec 100644 --- a/routers/private/hook.go +++ b/routers/private/hook.go @@ -15,16 +15,16 @@ import ( "strings" "code.gitea.io/gitea/models" + gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" pull_service "code.gitea.io/gitea/services/pull" repo_service "code.gitea.io/gitea/services/repository" - - "gitea.com/macaron/macaron" ) func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env []string) error { @@ -117,7 +117,8 @@ func isErrUnverifiedCommit(err error) bool { } // HookPreReceive checks whether a individual commit is acceptable -func HookPreReceive(ctx *macaron.Context, opts private.HookOptions) { +func HookPreReceive(ctx *gitea_context.PrivateContext) { + opts := web.GetForm(ctx).(*private.HookOptions) ownerName := ctx.Params(":owner") repoName := ctx.Params(":repo") repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName) @@ -370,7 +371,8 @@ func HookPreReceive(ctx *macaron.Context, opts private.HookOptions) { } // HookPostReceive updates services and users -func HookPostReceive(ctx *macaron.Context, opts private.HookOptions) { +func HookPostReceive(ctx *gitea_context.PrivateContext) { + opts := web.GetForm(ctx).(*private.HookOptions) ownerName := ctx.Params(":owner") repoName := ctx.Params(":repo") @@ -540,7 +542,7 @@ func HookPostReceive(ctx *macaron.Context, opts private.HookOptions) { } // SetDefaultBranch updates the default branch -func SetDefaultBranch(ctx *macaron.Context) { +func SetDefaultBranch(ctx *gitea_context.PrivateContext) { ownerName := ctx.Params(":owner") repoName := ctx.Params(":repo") branch := ctx.Params(":branch") diff --git a/routers/private/internal.go b/routers/private/internal.go index 4fb267a49a..e541591a38 100644 --- a/routers/private/internal.go +++ b/routers/private/internal.go @@ -6,47 +6,69 @@ package private import ( + "net/http" + "reflect" "strings" + "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" - "gitea.com/macaron/binding" - "gitea.com/macaron/macaron" + "gitea.com/go-chi/binding" ) // CheckInternalToken check internal token is set -func CheckInternalToken(ctx *macaron.Context) { - tokens := ctx.Req.Header.Get("Authorization") - fields := strings.Fields(tokens) - if len(fields) != 2 || fields[0] != "Bearer" || fields[1] != setting.InternalToken { - log.Debug("Forbidden attempt to access internal url: Authorization header: %s", tokens) - ctx.Error(403) +func CheckInternalToken(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + tokens := req.Header.Get("Authorization") + fields := strings.Fields(tokens) + if len(fields) != 2 || fields[0] != "Bearer" || fields[1] != setting.InternalToken { + log.Debug("Forbidden attempt to access internal url: Authorization header: %s", tokens) + http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden) + } else { + next.ServeHTTP(w, req) + } + }) +} + +// bind binding an obj to a handler +func bind(obj interface{}) http.HandlerFunc { + var tp = reflect.TypeOf(obj) + for tp.Kind() == reflect.Ptr { + tp = tp.Elem() } + return web.Wrap(func(ctx *context.PrivateContext) { + var theObj = reflect.New(tp).Interface() // create a new form obj for every request but not use obj directly + binding.Bind(ctx.Req, theObj) + web.SetForm(ctx, theObj) + }) } -// RegisterRoutes registers all internal APIs routes to web application. +// Routes registers all internal APIs routes to web application. // These APIs will be invoked by internal commands for example `gitea serv` and etc. -func RegisterRoutes(m *macaron.Macaron) { - bind := binding.Bind - - m.Group("/", func() { - m.Post("/ssh/authorized_keys", AuthorizedPublicKeyByContent) - m.Post("/ssh/:id/update/:repoid", UpdatePublicKeyInRepo) - m.Post("/hook/pre-receive/:owner/:repo", bind(private.HookOptions{}), HookPreReceive) - m.Post("/hook/post-receive/:owner/:repo", bind(private.HookOptions{}), HookPostReceive) - m.Post("/hook/set-default-branch/:owner/:repo/:branch", SetDefaultBranch) - m.Get("/serv/none/:keyid", ServNoCommand) - m.Get("/serv/command/:keyid/:owner/:repo", ServCommand) - m.Post("/manager/shutdown", Shutdown) - m.Post("/manager/restart", Restart) - m.Post("/manager/flush-queues", bind(private.FlushOptions{}), FlushQueues) - m.Post("/manager/pause-logging", PauseLogging) - m.Post("/manager/resume-logging", ResumeLogging) - m.Post("/manager/release-and-reopen-logging", ReleaseReopenLogging) - m.Post("/manager/add-logger", bind(private.LoggerOptions{}), AddLogger) - m.Post("/manager/remove-logger/:group/:name", RemoveLogger) - m.Post("/mail/send", SendEmail) - }, CheckInternalToken) +func Routes() *web.Route { + var r = web.NewRoute() + r.Use(context.PrivateContexter()) + r.Use(CheckInternalToken) + + r.Post("/ssh/authorized_keys", AuthorizedPublicKeyByContent) + r.Post("/ssh/{id}/update/{repoid}", UpdatePublicKeyInRepo) + r.Post("/hook/pre-receive/{owner}/{repo}", bind(private.HookOptions{}), HookPreReceive) + r.Post("/hook/post-receive/{owner}/{repo}", bind(private.HookOptions{}), HookPostReceive) + r.Post("/hook/set-default-branch/{owner}/{repo}/{branch}", SetDefaultBranch) + r.Get("/serv/none/{keyid}", ServNoCommand) + r.Get("/serv/command/{keyid}/{owner}/{repo}", ServCommand) + r.Post("/manager/shutdown", Shutdown) + r.Post("/manager/restart", Restart) + r.Post("/manager/flush-queues", bind(private.FlushOptions{}), FlushQueues) + r.Post("/manager/pause-logging", PauseLogging) + r.Post("/manager/resume-logging", ResumeLogging) + r.Post("/manager/release-and-reopen-logging", ReleaseReopenLogging) + r.Post("/manager/add-logger", bind(private.LoggerOptions{}), AddLogger) + r.Post("/manager/remove-logger/{group}/{name}", RemoveLogger) + r.Post("/mail/send", SendEmail) + + return r } diff --git a/routers/private/key.go b/routers/private/key.go index c00330fe88..b90faa22a4 100644 --- a/routers/private/key.go +++ b/routers/private/key.go @@ -9,13 +9,12 @@ import ( "net/http" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/timeutil" - - "gitea.com/macaron/macaron" ) // UpdatePublicKeyInRepo update public key and deploy key updates -func UpdatePublicKeyInRepo(ctx *macaron.Context) { +func UpdatePublicKeyInRepo(ctx *context.PrivateContext) { keyID := ctx.ParamsInt64(":id") repoID := ctx.ParamsInt64(":repoid") if err := models.UpdatePublicKeyUpdated(keyID); err != nil { @@ -49,7 +48,7 @@ func UpdatePublicKeyInRepo(ctx *macaron.Context) { // AuthorizedPublicKeyByContent searches content as prefix (leak e-mail part) // and returns public key found. -func AuthorizedPublicKeyByContent(ctx *macaron.Context) { +func AuthorizedPublicKeyByContent(ctx *context.PrivateContext) { content := ctx.Query("content") publicKey, err := models.SearchPublicKeyByContent(content) diff --git a/routers/private/mail.go b/routers/private/mail.go index b3b21d042f..330de14c46 100644 --- a/routers/private/mail.go +++ b/routers/private/mail.go @@ -11,17 +11,17 @@ import ( "strconv" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/services/mailer" - "gitea.com/macaron/macaron" ) // SendEmail pushes messages to mail queue // // It doesn't wait before each message will be processed -func SendEmail(ctx *macaron.Context) { +func SendEmail(ctx *context.PrivateContext) { if setting.MailService == nil { ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ "err": "Mail service is not enabled.", @@ -30,7 +30,7 @@ func SendEmail(ctx *macaron.Context) { } var mail private.Email - rd := ctx.Req.Body().ReadCloser() + rd := ctx.Req.Body defer rd.Close() if err := json.NewDecoder(rd).Decode(&mail); err != nil { log.Error("%v", err) @@ -77,7 +77,7 @@ func SendEmail(ctx *macaron.Context) { sendEmail(ctx, mail.Subject, mail.Message, emails) } -func sendEmail(ctx *macaron.Context, subject, message string, to []string) { +func sendEmail(ctx *context.PrivateContext, subject, message string, to []string) { for _, email := range to { msg := mailer.NewMessage([]string{email}, subject, message) mailer.SendAsync(msg) diff --git a/routers/private/manager.go b/routers/private/manager.go index 67bd92003f..e5b4583fd1 100644 --- a/routers/private/manager.go +++ b/routers/private/manager.go @@ -9,17 +9,18 @@ import ( "fmt" "net/http" + "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/queue" "code.gitea.io/gitea/modules/setting" - - "gitea.com/macaron/macaron" + "code.gitea.io/gitea/modules/web" ) // FlushQueues flushes all the Queues -func FlushQueues(ctx *macaron.Context, opts private.FlushOptions) { +func FlushQueues(ctx *context.PrivateContext) { + opts := web.GetForm(ctx).(*private.FlushOptions) if opts.NonBlocking { // Save the hammer ctx here - as a new one is created each time you call this. baseCtx := graceful.GetManager().HammerContext() @@ -34,7 +35,7 @@ func FlushQueues(ctx *macaron.Context, opts private.FlushOptions) { }) return } - err := queue.GetManager().FlushAll(ctx.Req.Request.Context(), opts.Timeout) + err := queue.GetManager().FlushAll(ctx.Req.Context(), opts.Timeout) if err != nil { ctx.JSON(http.StatusRequestTimeout, map[string]interface{}{ "err": fmt.Sprintf("%v", err), @@ -44,19 +45,19 @@ func FlushQueues(ctx *macaron.Context, opts private.FlushOptions) { } // PauseLogging pauses logging -func PauseLogging(ctx *macaron.Context) { +func PauseLogging(ctx *context.PrivateContext) { log.Pause() ctx.PlainText(http.StatusOK, []byte("success")) } // ResumeLogging resumes logging -func ResumeLogging(ctx *macaron.Context) { +func ResumeLogging(ctx *context.PrivateContext) { log.Resume() ctx.PlainText(http.StatusOK, []byte("success")) } // ReleaseReopenLogging releases and reopens logging files -func ReleaseReopenLogging(ctx *macaron.Context) { +func ReleaseReopenLogging(ctx *context.PrivateContext) { if err := log.ReleaseReopen(); err != nil { ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ "err": fmt.Sprintf("Error during release and reopen: %v", err), @@ -67,7 +68,7 @@ func ReleaseReopenLogging(ctx *macaron.Context) { } // RemoveLogger removes a logger -func RemoveLogger(ctx *macaron.Context) { +func RemoveLogger(ctx *context.PrivateContext) { group := ctx.Params("group") name := ctx.Params("name") ok, err := log.GetLogger(group).DelLogger(name) @@ -84,7 +85,8 @@ func RemoveLogger(ctx *macaron.Context) { } // AddLogger adds a logger -func AddLogger(ctx *macaron.Context, opts private.LoggerOptions) { +func AddLogger(ctx *context.PrivateContext) { + opts := web.GetForm(ctx).(*private.LoggerOptions) if len(opts.Group) == 0 { opts.Group = log.DEFAULT } diff --git a/routers/private/manager_unix.go b/routers/private/manager_unix.go index ec5e976059..60ae9b68e8 100644 --- a/routers/private/manager_unix.go +++ b/routers/private/manager_unix.go @@ -9,20 +9,19 @@ package private import ( "net/http" + "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/graceful" - - "gitea.com/macaron/macaron" ) // Restart causes the server to perform a graceful restart -func Restart(ctx *macaron.Context) { +func Restart(ctx *context.PrivateContext) { graceful.GetManager().DoGracefulRestart() ctx.PlainText(http.StatusOK, []byte("success")) } // Shutdown causes the server to perform a graceful shutdown -func Shutdown(ctx *macaron.Context) { +func Shutdown(ctx *context.PrivateContext) { graceful.GetManager().DoGracefulShutdown() ctx.PlainText(http.StatusOK, []byte("success")) } diff --git a/routers/private/manager_windows.go b/routers/private/manager_windows.go index ac840a9d81..244dbbe4df 100644 --- a/routers/private/manager_windows.go +++ b/routers/private/manager_windows.go @@ -9,20 +9,19 @@ package private import ( "net/http" + "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/graceful" - - "gitea.com/macaron/macaron" ) // Restart is not implemented for Windows based servers as they can't fork -func Restart(ctx *macaron.Context) { +func Restart(ctx *context.PrivateContext) { ctx.JSON(http.StatusNotImplemented, map[string]interface{}{ "err": "windows servers cannot be gracefully restarted - shutdown and restart manually", }) } // Shutdown causes the server to perform a graceful shutdown -func Shutdown(ctx *macaron.Context) { +func Shutdown(ctx *context.PrivateContext) { graceful.GetManager().DoGracefulShutdown() ctx.PlainText(http.StatusOK, []byte("success")) } diff --git a/routers/private/serv.go b/routers/private/serv.go index 90e1d30b01..1461194e7f 100644 --- a/routers/private/serv.go +++ b/routers/private/serv.go @@ -11,17 +11,16 @@ import ( "strings" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/setting" repo_service "code.gitea.io/gitea/services/repository" wiki_service "code.gitea.io/gitea/services/wiki" - - "gitea.com/macaron/macaron" ) // ServNoCommand returns information about the provided keyid -func ServNoCommand(ctx *macaron.Context) { +func ServNoCommand(ctx *context.PrivateContext) { keyID := ctx.ParamsInt64(":keyid") if keyID <= 0 { ctx.JSON(http.StatusBadRequest, map[string]interface{}{ @@ -73,7 +72,7 @@ func ServNoCommand(ctx *macaron.Context) { } // ServCommand returns information about the provided keyid -func ServCommand(ctx *macaron.Context) { +func ServCommand(ctx *context.PrivateContext) { keyID := ctx.ParamsInt64(":keyid") ownerName := ctx.Params(":owner") repoName := ctx.Params(":repo") diff --git a/routers/repo/branch.go b/routers/repo/branch.go index bf9f2e6a36..7d844abe5a 100644 --- a/routers/repo/branch.go +++ b/routers/repo/branch.go @@ -10,14 +10,15 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/repofiles" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" repo_service "code.gitea.io/gitea/services/repository" ) @@ -367,7 +368,8 @@ func getDeletedBranches(ctx *context.Context) ([]*Branch, error) { } // CreateBranch creates new branch in repository -func CreateBranch(ctx *context.Context, form auth.NewBranchForm) { +func CreateBranch(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewBranchForm) if !ctx.Repo.CanCreateBranch() { ctx.NotFound("CreateBranch", nil) return diff --git a/routers/repo/editor.go b/routers/repo/editor.go index 0bc76504f9..619912fef7 100644 --- a/routers/repo/editor.go +++ b/routers/repo/editor.go @@ -12,10 +12,10 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/repofiles" @@ -23,6 +23,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" ) @@ -325,17 +326,20 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo } // EditFilePost response for editing file -func EditFilePost(ctx *context.Context, form auth.EditRepoFileForm) { - editFilePost(ctx, form, false) +func EditFilePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.EditRepoFileForm) + editFilePost(ctx, *form, false) } // NewFilePost response for creating file -func NewFilePost(ctx *context.Context, form auth.EditRepoFileForm) { - editFilePost(ctx, form, true) +func NewFilePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.EditRepoFileForm) + editFilePost(ctx, *form, true) } // DiffPreviewPost render preview diff page -func DiffPreviewPost(ctx *context.Context, form auth.EditPreviewDiffForm) { +func DiffPreviewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.EditPreviewDiffForm) treePath := cleanUploadFileName(ctx.Repo.TreePath) if len(treePath) == 0 { ctx.Error(500, "file name to diff is invalid") @@ -394,7 +398,8 @@ func DeleteFile(ctx *context.Context) { } // DeleteFilePost response for deleting file -func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) { +func DeleteFilePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.DeleteRepoFileForm) canCommit := renderCommitRights(ctx) branchName := ctx.Repo.BranchName if form.CommitChoice == frmCommitChoiceNewBranch { @@ -556,7 +561,8 @@ func UploadFile(ctx *context.Context) { } // UploadFilePost response for uploading file -func UploadFilePost(ctx *context.Context, form auth.UploadRepoFileForm) { +func UploadFilePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.UploadRepoFileForm) ctx.Data["PageIsUpload"] = true ctx.Data["RequireTribute"] = true ctx.Data["RequireSimpleMDE"] = true @@ -760,7 +766,8 @@ func UploadFileToServer(ctx *context.Context) { } // RemoveUploadFileFromServer remove file from server file dir -func RemoveUploadFileFromServer(ctx *context.Context, form auth.RemoveUploadFileForm) { +func RemoveUploadFileFromServer(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.RemoveUploadFileForm) if len(form.File) == 0 { ctx.Status(204) return diff --git a/routers/repo/http.go b/routers/repo/http.go index 3de45698e8..0377979e8b 100644 --- a/routers/repo/http.go +++ b/routers/repo/http.go @@ -35,8 +35,17 @@ import ( repo_service "code.gitea.io/gitea/services/repository" ) -// HTTP implmentation git smart HTTP protocol -func HTTP(ctx *context.Context) { +// httpBase implmentation git smart HTTP protocol +func httpBase(ctx *context.Context) (h *serviceHandler) { + if setting.Repository.DisableHTTPGit { + ctx.Resp.WriteHeader(http.StatusForbidden) + _, err := ctx.Resp.Write([]byte("Interacting with repositories by HTTP protocol is not allowed")) + if err != nil { + log.Error(err.Error()) + } + return + } + if len(setting.Repository.AccessControlAllowOrigin) > 0 { allowedOrigin := setting.Repository.AccessControlAllowOrigin // Set CORS headers for browser-based git clients @@ -344,7 +353,7 @@ func HTTP(ctx *context.Context) { environ = append(environ, models.EnvRepoID+fmt.Sprintf("=%d", repo.ID)) w := ctx.Resp - r := ctx.Req.Request + r := ctx.Req cfg := &serviceConfig{ UploadPack: true, ReceivePack: true, @@ -353,47 +362,9 @@ func HTTP(ctx *context.Context) { r.URL.Path = strings.ToLower(r.URL.Path) // blue: In case some repo name has upper case name - for _, route := range routes { - if m := route.reg.FindStringSubmatch(r.URL.Path); m != nil { - if setting.Repository.DisableHTTPGit { - w.WriteHeader(http.StatusForbidden) - _, err := w.Write([]byte("Interacting with repositories by HTTP protocol is not allowed")) - if err != nil { - log.Error(err.Error()) - } - return - } - if route.method != r.Method { - if r.Proto == "HTTP/1.1" { - w.WriteHeader(http.StatusMethodNotAllowed) - _, err := w.Write([]byte("Method Not Allowed")) - if err != nil { - log.Error(err.Error()) - } - } else { - w.WriteHeader(http.StatusBadRequest) - _, err := w.Write([]byte("Bad Request")) - if err != nil { - log.Error(err.Error()) - } - } - return - } - - file := strings.Replace(r.URL.Path, m[1]+"/", "", 1) - dir, err := getGitRepoPath(m[1]) - if err != nil { - log.Error(err.Error()) - ctx.NotFound("Smart Git HTTP", err) - return - } - - route.handler(serviceHandler{cfg, w, r, dir, file, cfg.Env}) - return - } - } + dir := models.RepoPath(username, reponame) - ctx.NotFound("Smart Git HTTP", nil) + return &serviceHandler{cfg, w, r, dir, cfg.Env} } var ( @@ -449,7 +420,6 @@ type serviceHandler struct { w http.ResponseWriter r *http.Request dir string - file string environ []string } @@ -467,8 +437,8 @@ func (h *serviceHandler) setHeaderCacheForever() { h.w.Header().Set("Cache-Control", "public, max-age=31536000") } -func (h *serviceHandler) sendFile(contentType string) { - reqFile := path.Join(h.dir, h.file) +func (h *serviceHandler) sendFile(contentType, file string) { + reqFile := path.Join(h.dir, file) fi, err := os.Stat(reqFile) if os.IsNotExist(err) { @@ -482,26 +452,6 @@ func (h *serviceHandler) sendFile(contentType string) { http.ServeFile(h.w, h.r, reqFile) } -type route struct { - reg *regexp.Regexp - method string - handler func(serviceHandler) -} - -var routes = []route{ - {regexp.MustCompile(`(.*?)/git-upload-pack$`), "POST", serviceUploadPack}, - {regexp.MustCompile(`(.*?)/git-receive-pack$`), "POST", serviceReceivePack}, - {regexp.MustCompile(`(.*?)/info/refs$`), "GET", getInfoRefs}, - {regexp.MustCompile(`(.*?)/HEAD$`), "GET", getTextFile}, - {regexp.MustCompile(`(.*?)/objects/info/alternates$`), "GET", getTextFile}, - {regexp.MustCompile(`(.*?)/objects/info/http-alternates$`), "GET", getTextFile}, - {regexp.MustCompile(`(.*?)/objects/info/packs$`), "GET", getInfoPacks}, - {regexp.MustCompile(`(.*?)/objects/info/[^/]*$`), "GET", getTextFile}, - {regexp.MustCompile(`(.*?)/objects/[0-9a-f]{2}/[0-9a-f]{38}$`), "GET", getLooseObject}, - {regexp.MustCompile(`(.*?)/objects/pack/pack-[0-9a-f]{40}\.pack$`), "GET", getPackFile}, - {regexp.MustCompile(`(.*?)/objects/pack/pack-[0-9a-f]{40}\.idx$`), "GET", getIdxFile}, -} - // one or more key=value pairs separated by colons var safeGitProtocolHeader = regexp.MustCompile(`^[0-9a-zA-Z]+=[0-9a-zA-Z]+(:[0-9a-zA-Z]+=[0-9a-zA-Z]+)*$`) @@ -598,12 +548,20 @@ func serviceRPC(h serviceHandler, service string) { } } -func serviceUploadPack(h serviceHandler) { - serviceRPC(h, "upload-pack") +// ServiceUploadPack implements Git Smart HTTP protocol +func ServiceUploadPack(ctx *context.Context) { + h := httpBase(ctx) + if h != nil { + serviceRPC(*h, "upload-pack") + } } -func serviceReceivePack(h serviceHandler) { - serviceRPC(h, "receive-pack") +// ServiceReceivePack implements Git Smart HTTP protocol +func ServiceReceivePack(ctx *context.Context) { + h := httpBase(ctx) + if h != nil { + serviceRPC(*h, "receive-pack") + } } func getServiceType(r *http.Request) string { @@ -630,9 +588,14 @@ func packetWrite(str string) []byte { return []byte(s + str) } -func getInfoRefs(h serviceHandler) { +// GetInfoRefs implements Git dumb HTTP +func GetInfoRefs(ctx *context.Context) { + h := httpBase(ctx) + if h == nil { + return + } h.setHeaderNoCache() - if hasAccess(getServiceType(h.r), h, false) { + if hasAccess(getServiceType(h.r), *h, false) { service := getServiceType(h.r) if protocol := h.r.Header.Get("Git-Protocol"); protocol != "" && safeGitProtocolHeader.MatchString(protocol) { @@ -652,44 +615,59 @@ func getInfoRefs(h serviceHandler) { _, _ = h.w.Write(refs) } else { updateServerInfo(h.dir) - h.sendFile("text/plain; charset=utf-8") + h.sendFile("text/plain; charset=utf-8", "info/refs") } } -func getTextFile(h serviceHandler) { - h.setHeaderNoCache() - h.sendFile("text/plain") -} - -func getInfoPacks(h serviceHandler) { - h.setHeaderCacheForever() - h.sendFile("text/plain; charset=utf-8") -} - -func getLooseObject(h serviceHandler) { - h.setHeaderCacheForever() - h.sendFile("application/x-git-loose-object") +// GetTextFile implements Git dumb HTTP +func GetTextFile(p string) func(*context.Context) { + return func(ctx *context.Context) { + h := httpBase(ctx) + if h != nil { + h.setHeaderNoCache() + file := ctx.Params("file") + if file != "" { + h.sendFile("text/plain", "objects/info/"+file) + } else { + h.sendFile("text/plain", p) + } + } + } } -func getPackFile(h serviceHandler) { - h.setHeaderCacheForever() - h.sendFile("application/x-git-packed-objects") +// GetInfoPacks implements Git dumb HTTP +func GetInfoPacks(ctx *context.Context) { + h := httpBase(ctx) + if h != nil { + h.setHeaderCacheForever() + h.sendFile("text/plain; charset=utf-8", "objects/info/packs") + } } -func getIdxFile(h serviceHandler) { - h.setHeaderCacheForever() - h.sendFile("application/x-git-packed-objects-toc") +// GetLooseObject implements Git dumb HTTP +func GetLooseObject(ctx *context.Context) { + h := httpBase(ctx) + if h != nil { + h.setHeaderCacheForever() + h.sendFile("application/x-git-loose-object", fmt.Sprintf("objects/%s/%s", + ctx.Params("head"), ctx.Params("hash"))) + } } -func getGitRepoPath(subdir string) (string, error) { - if !strings.HasSuffix(subdir, ".git") { - subdir += ".git" +// GetPackFile implements Git dumb HTTP +func GetPackFile(ctx *context.Context) { + h := httpBase(ctx) + if h != nil { + h.setHeaderCacheForever() + h.sendFile("application/x-git-packed-objects", "objects/pack/pack-"+ctx.Params("file")+".pack") } +} - fpath := path.Join(setting.RepoRootPath, subdir) - if _, err := os.Stat(fpath); os.IsNotExist(err) { - return "", err +// GetIdxFile implements Git dumb HTTP +func GetIdxFile(ctx *context.Context) { + h := httpBase(ctx) + if h != nil { + h.setHeaderCacheForever() + h.sendFile("application/x-git-packed-objects-toc", "objects/pack/pack-"+ctx.Params("file")+".idx") } - - return fpath, nil } diff --git a/routers/repo/issue.go b/routers/repo/issue.go index fbeae75ab5..fb7107451f 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -16,10 +16,10 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" issue_indexer "code.gitea.io/gitea/modules/indexer/issues" "code.gitea.io/gitea/modules/log" @@ -29,6 +29,7 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" comment_service "code.gitea.io/gitea/services/comments" issue_service "code.gitea.io/gitea/services/issue" pull_service "code.gitea.io/gitea/services/pull" @@ -924,7 +925,8 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm, isPull b } // NewIssuePost response for creating new issue -func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) { +func NewIssuePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateIssueForm) ctx.Data["Title"] = ctx.Tr("repo.issues.new") ctx.Data["PageIsIssueList"] = true ctx.Data["NewIssueChooseTemplate"] = len(ctx.IssueTemplatesFromDefaultBranch()) > 0 @@ -940,7 +942,7 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) { attachments []string ) - labelIDs, assigneeIDs, milestoneID, projectID := ValidateRepoMetas(ctx, form, false) + labelIDs, assigneeIDs, milestoneID, projectID := ValidateRepoMetas(ctx, *form, false) if ctx.Written() { return } @@ -1925,7 +1927,8 @@ func UpdateIssueStatus(ctx *context.Context) { } // NewComment create a comment for issue -func NewComment(ctx *context.Context, form auth.CreateCommentForm) { +func NewComment(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateCommentForm) issue := GetActionIssue(ctx) if ctx.Written() { return @@ -2134,7 +2137,8 @@ func DeleteComment(ctx *context.Context) { } // ChangeIssueReaction create a reaction for issue -func ChangeIssueReaction(ctx *context.Context, form auth.ReactionForm) { +func ChangeIssueReaction(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.ReactionForm) issue := GetActionIssue(ctx) if ctx.Written() { return @@ -2229,7 +2233,8 @@ func ChangeIssueReaction(ctx *context.Context, form auth.ReactionForm) { } // ChangeCommentReaction create a reaction for comment -func ChangeCommentReaction(ctx *context.Context, form auth.ReactionForm) { +func ChangeCommentReaction(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.ReactionForm) comment, err := models.GetCommentByID(ctx.ParamsInt64(":id")) if err != nil { ctx.NotFoundOrServerError("GetCommentByID", models.IsErrCommentNotExist, err) diff --git a/routers/repo/issue_label.go b/routers/repo/issue_label.go index f1e188fe3a..35035103d5 100644 --- a/routers/repo/issue_label.go +++ b/routers/repo/issue_label.go @@ -6,11 +6,12 @@ package repo import ( "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" issue_service "code.gitea.io/gitea/services/issue" ) @@ -29,7 +30,8 @@ func Labels(ctx *context.Context) { } // InitializeLabels init labels for a repository -func InitializeLabels(ctx *context.Context, form auth.InitializeLabelsForm) { +func InitializeLabels(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.InitializeLabelsForm) if ctx.HasError() { ctx.Redirect(ctx.Repo.RepoLink + "/labels") return @@ -94,7 +96,8 @@ func RetrieveLabels(ctx *context.Context) { } // NewLabel create new label for repository -func NewLabel(ctx *context.Context, form auth.CreateLabelForm) { +func NewLabel(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateLabelForm) ctx.Data["Title"] = ctx.Tr("repo.labels") ctx.Data["PageIsLabels"] = true @@ -118,7 +121,8 @@ func NewLabel(ctx *context.Context, form auth.CreateLabelForm) { } // UpdateLabel update a label's name and color -func UpdateLabel(ctx *context.Context, form auth.CreateLabelForm) { +func UpdateLabel(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateLabelForm) l, err := models.GetLabelInRepoByID(ctx.Repo.Repository.ID, form.ID) if err != nil { switch { diff --git a/routers/repo/issue_label_test.go b/routers/repo/issue_label_test.go index bf62511258..d67c70085d 100644 --- a/routers/repo/issue_label_test.go +++ b/routers/repo/issue_label_test.go @@ -10,8 +10,9 @@ import ( "testing" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/modules/web" "github.com/stretchr/testify/assert" ) @@ -32,7 +33,8 @@ func TestInitializeLabels(t *testing.T) { ctx := test.MockContext(t, "user2/repo1/labels/initialize") test.LoadUser(t, ctx, 2) test.LoadRepo(t, ctx, 2) - InitializeLabels(ctx, auth.InitializeLabelsForm{TemplateName: "Default"}) + web.SetForm(ctx, &auth.InitializeLabelsForm{TemplateName: "Default"}) + InitializeLabels(ctx) assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) models.AssertExistsAndLoadBean(t, &models.Label{ RepoID: 2, @@ -74,10 +76,11 @@ func TestNewLabel(t *testing.T) { ctx := test.MockContext(t, "user2/repo1/labels/edit") test.LoadUser(t, ctx, 2) test.LoadRepo(t, ctx, 1) - NewLabel(ctx, auth.CreateLabelForm{ + web.SetForm(ctx, &auth.CreateLabelForm{ Title: "newlabel", Color: "#abcdef", }) + NewLabel(ctx) assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) models.AssertExistsAndLoadBean(t, &models.Label{ Name: "newlabel", @@ -91,11 +94,12 @@ func TestUpdateLabel(t *testing.T) { ctx := test.MockContext(t, "user2/repo1/labels/edit") test.LoadUser(t, ctx, 2) test.LoadRepo(t, ctx, 1) - UpdateLabel(ctx, auth.CreateLabelForm{ + web.SetForm(ctx, &auth.CreateLabelForm{ ID: 2, Title: "newnameforlabel", Color: "#abcdef", }) + UpdateLabel(ctx) assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) models.AssertExistsAndLoadBean(t, &models.Label{ ID: 2, diff --git a/routers/repo/issue_lock.go b/routers/repo/issue_lock.go index fa87588319..f8131e46aa 100644 --- a/routers/repo/issue_lock.go +++ b/routers/repo/issue_lock.go @@ -8,14 +8,15 @@ import ( "net/http" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" + "code.gitea.io/gitea/modules/web" ) // LockIssue locks an issue. This would limit commenting abilities to // users with write access to the repo. -func LockIssue(ctx *context.Context, form auth.IssueLockForm) { - +func LockIssue(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.IssueLockForm) issue := GetActionIssue(ctx) if ctx.Written() { return diff --git a/routers/repo/issue_timetrack.go b/routers/repo/issue_timetrack.go index 0f711bc734..425f215110 100644 --- a/routers/repo/issue_timetrack.go +++ b/routers/repo/issue_timetrack.go @@ -9,12 +9,14 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" + "code.gitea.io/gitea/modules/web" ) // AddTimeManually tracks time manually -func AddTimeManually(c *context.Context, form auth.AddTimeManuallyForm) { +func AddTimeManually(c *context.Context) { + form := web.GetForm(c).(*auth.AddTimeManuallyForm) issue := GetActionIssue(c) if c.Written() { return diff --git a/routers/repo/migrate.go b/routers/repo/migrate.go index a628fd2e2f..89452de0fa 100644 --- a/routers/repo/migrate.go +++ b/routers/repo/migrate.go @@ -10,14 +10,15 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/migrations" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/task" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" ) const ( @@ -117,7 +118,8 @@ func handleMigrateError(ctx *context.Context, owner *models.User, err error, nam } // MigratePost response for migrating from external git repository -func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) { +func MigratePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.MigrateRepoForm) if setting.Repository.DisableMigrations { ctx.Error(http.StatusForbidden, "MigratePost: the site administrator has disabled migrations") return @@ -192,7 +194,7 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) { err = models.CheckCreateRepository(ctx.User, ctxUser, opts.RepoName, false) if err != nil { - handleMigrateError(ctx, ctxUser, err, "MigratePost", tpl, &form) + handleMigrateError(ctx, ctxUser, err, "MigratePost", tpl, form) return } @@ -202,5 +204,5 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) { return } - handleMigrateError(ctx, ctxUser, err, "MigratePost", tpl, &form) + handleMigrateError(ctx, ctxUser, err, "MigratePost", tpl, form) } diff --git a/routers/repo/milestone.go b/routers/repo/milestone.go index 96f5b4e5f0..a9beed75d7 100644 --- a/routers/repo/milestone.go +++ b/routers/repo/milestone.go @@ -8,14 +8,15 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "xorm.io/builder" ) @@ -106,7 +107,8 @@ func NewMilestone(ctx *context.Context) { } // NewMilestonePost response for creating milestone -func NewMilestonePost(ctx *context.Context, form auth.CreateMilestoneForm) { +func NewMilestonePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateMilestoneForm) ctx.Data["Title"] = ctx.Tr("repo.milestones.new") ctx.Data["PageIsIssueList"] = true ctx.Data["PageIsMilestones"] = true @@ -165,7 +167,8 @@ func EditMilestone(ctx *context.Context) { } // EditMilestonePost response for edting milestone -func EditMilestonePost(ctx *context.Context, form auth.CreateMilestoneForm) { +func EditMilestonePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateMilestoneForm) ctx.Data["Title"] = ctx.Tr("repo.milestones.edit") ctx.Data["PageIsMilestones"] = true ctx.Data["PageIsEditMilestone"] = true diff --git a/routers/repo/projects.go b/routers/repo/projects.go index 4cff199b34..49bcfef0ce 100644 --- a/routers/repo/projects.go +++ b/routers/repo/projects.go @@ -9,12 +9,13 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" ) const ( @@ -112,7 +113,8 @@ func NewProject(ctx *context.Context) { } // NewProjectPost creates a new project -func NewProjectPost(ctx *context.Context, form auth.CreateProjectForm) { +func NewProjectPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateProjectForm) ctx.Data["Title"] = ctx.Tr("repo.projects.new") if ctx.HasError() { @@ -217,7 +219,8 @@ func EditProject(ctx *context.Context) { } // EditProjectPost response for editing a project -func EditProjectPost(ctx *context.Context, form auth.CreateProjectForm) { +func EditProjectPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateProjectForm) ctx.Data["Title"] = ctx.Tr("repo.projects.edit") ctx.Data["PageIsProjects"] = true ctx.Data["PageIsEditProjects"] = true @@ -399,8 +402,8 @@ func DeleteProjectBoard(ctx *context.Context) { } // AddBoardToProjectPost allows a new board to be added to a project. -func AddBoardToProjectPost(ctx *context.Context, form auth.EditProjectBoardTitleForm) { - +func AddBoardToProjectPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.EditProjectBoardTitleForm) if !ctx.Repo.IsOwner() && !ctx.Repo.IsAdmin() && !ctx.Repo.CanAccess(models.AccessModeWrite, models.UnitTypeProjects) { ctx.JSON(403, map[string]string{ "message": "Only authorized users are allowed to perform this action.", @@ -479,8 +482,8 @@ func checkProjectBoardChangePermissions(ctx *context.Context) (*models.Project, } // EditProjectBoardTitle allows a project board's title to be updated -func EditProjectBoardTitle(ctx *context.Context, form auth.EditProjectBoardTitleForm) { - +func EditProjectBoardTitle(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.EditProjectBoardTitleForm) _, board := checkProjectBoardChangePermissions(ctx) if ctx.Written() { return diff --git a/routers/repo/pull.go b/routers/repo/pull.go index 01c6efaa1d..1862c15f43 100644 --- a/routers/repo/pull.go +++ b/routers/repo/pull.go @@ -16,17 +16,19 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/middlewares" "code.gitea.io/gitea/modules/notification" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" "code.gitea.io/gitea/services/gitdiff" pull_service "code.gitea.io/gitea/services/pull" @@ -168,7 +170,8 @@ func Fork(ctx *context.Context) { } // ForkPost response for forking a repository -func ForkPost(ctx *context.Context, form auth.CreateRepoForm) { +func ForkPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateRepoForm) ctx.Data["Title"] = ctx.Tr("new_fork") ctxUser := checkContextUser(ctx, form.UID) @@ -765,7 +768,8 @@ func UpdatePullRequest(ctx *context.Context) { } // MergePullRequest response for merging pull request -func MergePullRequest(ctx *context.Context, form auth.MergePullRequestForm) { +func MergePullRequest(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.MergePullRequestForm) issue := checkPullInfo(ctx) if ctx.Written() { return @@ -954,7 +958,8 @@ func stopTimerIfAvailable(user *models.User, issue *models.Issue) error { } // CompareAndPullRequestPost response for creating pull request -func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm) { +func CompareAndPullRequestPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateIssueForm) ctx.Data["Title"] = ctx.Tr("repo.pulls.compare_changes") ctx.Data["PageIsComparePull"] = true ctx.Data["IsDiffCompare"] = true @@ -974,7 +979,7 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm) } defer headGitRepo.Close() - labelIDs, assigneeIDs, milestoneID, _ := ValidateRepoMetas(ctx, form, true) + labelIDs, assigneeIDs, milestoneID, _ := ValidateRepoMetas(ctx, *form, true) if ctx.Written() { return } @@ -984,7 +989,7 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm) } if ctx.HasError() { - auth.AssignForm(form, ctx.Data) + middlewares.AssignForm(form, ctx.Data) // This stage is already stop creating new pull request, so it does not matter if it has // something to compare or not. diff --git a/routers/repo/pull_review.go b/routers/repo/pull_review.go index 0bacc68232..df49b6cfe1 100644 --- a/routers/repo/pull_review.go +++ b/routers/repo/pull_review.go @@ -8,10 +8,11 @@ import ( "fmt" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/web" pull_service "code.gitea.io/gitea/services/pull" ) @@ -44,7 +45,8 @@ func RenderNewCodeCommentForm(ctx *context.Context) { } // CreateCodeComment will create a code comment including an pending review if required -func CreateCodeComment(ctx *context.Context, form auth.CodeCommentForm) { +func CreateCodeComment(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CodeCommentForm) issue := GetActionIssue(ctx) if !issue.IsPull { return @@ -171,7 +173,8 @@ func renderConversation(ctx *context.Context, comment *models.Comment) { } // SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist -func SubmitReview(ctx *context.Context, form auth.SubmitReviewForm) { +func SubmitReview(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.SubmitReviewForm) issue := GetActionIssue(ctx) if !issue.IsPull { return diff --git a/routers/repo/release.go b/routers/repo/release.go index 4d75c37c87..54642f9b21 100644 --- a/routers/repo/release.go +++ b/routers/repo/release.go @@ -9,14 +9,15 @@ import ( "fmt" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/upload" + "code.gitea.io/gitea/modules/web" releaseservice "code.gitea.io/gitea/services/release" ) @@ -230,7 +231,8 @@ func NewRelease(ctx *context.Context) { } // NewReleasePost response for creating a release -func NewReleasePost(ctx *context.Context, form auth.NewReleaseForm) { +func NewReleasePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewReleaseForm) ctx.Data["Title"] = ctx.Tr("repo.release.new_release") ctx.Data["PageIsReleaseList"] = true @@ -336,7 +338,8 @@ func EditRelease(ctx *context.Context) { } // EditReleasePost response for edit release -func EditReleasePost(ctx *context.Context, form auth.EditReleaseForm) { +func EditReleasePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.EditReleaseForm) ctx.Data["Title"] = ctx.Tr("repo.release.edit_release") ctx.Data["PageIsReleaseList"] = true ctx.Data["PageIsEditRelease"] = true diff --git a/routers/repo/release_test.go b/routers/repo/release_test.go index 47d1a89b54..38c0d9fec0 100644 --- a/routers/repo/release_test.go +++ b/routers/repo/release_test.go @@ -8,8 +8,9 @@ import ( "testing" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/modules/web" ) func TestNewReleasePost(t *testing.T) { @@ -48,7 +49,8 @@ func TestNewReleasePost(t *testing.T) { test.LoadUser(t, ctx, 2) test.LoadRepo(t, ctx, 1) test.LoadGitRepo(t, ctx) - NewReleasePost(ctx, testCase.Form) + web.SetForm(ctx, &testCase.Form) + NewReleasePost(ctx) models.AssertExistsAndLoadBean(t, &models.Release{ RepoID: 1, PublisherID: 2, diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 3832b89971..a8cfb9ad7c 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -11,11 +11,12 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" archiver_service "code.gitea.io/gitea/services/archiver" repo_service "code.gitea.io/gitea/services/repository" ) @@ -181,7 +182,8 @@ func handleCreateError(ctx *context.Context, owner *models.User, err error, name } // CreatePost response for creating repository -func CreatePost(ctx *context.Context, form auth.CreateRepoForm) { +func CreatePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.CreateRepoForm) ctx.Data["Title"] = ctx.Tr("new_repo") ctx.Data["Gitignores"] = models.Gitignores diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 368879234b..3e22e8804e 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -15,9 +15,9 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/repository" @@ -25,6 +25,7 @@ import ( "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/validation" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" "code.gitea.io/gitea/services/mailer" mirror_service "code.gitea.io/gitea/services/mirror" @@ -59,7 +60,8 @@ func Settings(ctx *context.Context) { } // SettingsPost response for changes of a repository -func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { +func SettingsPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.RepoSettingForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsOptions"] = true @@ -839,7 +841,8 @@ func DeployKeys(ctx *context.Context) { } // DeployKeysPost response for adding a deploy key of a repository -func DeployKeysPost(ctx *context.Context, form auth.AddKeyForm) { +func DeployKeysPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AddKeyForm) ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys") ctx.Data["PageIsSettingsKeys"] = true @@ -956,9 +959,10 @@ func UpdateAvatarSetting(ctx *context.Context, form auth.AvatarForm) error { } // SettingsAvatar save new POSTed repository avatar -func SettingsAvatar(ctx *context.Context, form auth.AvatarForm) { +func SettingsAvatar(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AvatarForm) form.Source = auth.AvatarLocal - if err := UpdateAvatarSetting(ctx, form); err != nil { + if err := UpdateAvatarSetting(ctx, *form); err != nil { ctx.Flash.Error(err.Error()) } else { ctx.Flash.Success(ctx.Tr("repo.settings.update_avatar_success")) diff --git a/routers/repo/setting_protected_branch.go b/routers/repo/setting_protected_branch.go index c2e7bc8fac..017054d4c2 100644 --- a/routers/repo/setting_protected_branch.go +++ b/routers/repo/setting_protected_branch.go @@ -10,12 +10,13 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" pull_service "code.gitea.io/gitea/services/pull" ) @@ -168,7 +169,8 @@ func SettingsProtectedBranch(c *context.Context) { } // SettingsProtectedBranchPost updates the protected branch settings -func SettingsProtectedBranchPost(ctx *context.Context, f auth.ProtectBranchForm) { +func SettingsProtectedBranchPost(ctx *context.Context) { + f := web.GetForm(ctx).(*auth.ProtectBranchForm) branch := ctx.Params("*") if !ctx.Repo.GitRepo.IsBranchExist(branch) { ctx.NotFound("IsBranchExist", nil) diff --git a/routers/repo/settings_test.go b/routers/repo/settings_test.go index 679bb0d33c..85515121c7 100644 --- a/routers/repo/settings_test.go +++ b/routers/repo/settings_test.go @@ -10,11 +10,12 @@ import ( "testing" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "github.com/stretchr/testify/assert" ) @@ -52,7 +53,8 @@ func TestAddReadOnlyDeployKey(t *testing.T) { Title: "read-only", Content: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC4cn+iXnA4KvcQYSV88vGn0Yi91vG47t1P7okprVmhNTkipNRIHWr6WdCO4VDr/cvsRkuVJAsLO2enwjGWWueOO6BodiBgyAOZ/5t5nJNMCNuLGT5UIo/RI1b0WRQwxEZTRjt6mFNw6lH14wRd8ulsr9toSWBPMOGWoYs1PDeDL0JuTjL+tr1SZi/EyxCngpYszKdXllJEHyI79KQgeD0Vt3pTrkbNVTOEcCNqZePSVmUH8X8Vhugz3bnE0/iE9Pb5fkWO9c4AnM1FgI/8Bvp27Fw2ShryIXuR6kKvUqhVMTuOSDHwu6A8jLE5Owt3GAYugDpDYuwTVNGrHLXKpPzrGGPE/jPmaLCMZcsdkec95dYeU3zKODEm8UQZFhmJmDeWVJ36nGrGZHL4J5aTTaeFUJmmXDaJYiJ+K2/ioKgXqnXvltu0A9R8/LGy4nrTJRr4JMLuJFoUXvGm1gXQ70w2LSpk6yl71RNC0hCtsBe8BP8IhYCM0EP5jh7eCMQZNvM= nocomment\n", } - DeployKeysPost(ctx, addKeyForm) + web.SetForm(ctx, &addKeyForm) + DeployKeysPost(ctx) assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) models.AssertExistsAndLoadBean(t, &models.DeployKey{ @@ -81,7 +83,8 @@ func TestAddReadWriteOnlyDeployKey(t *testing.T) { Content: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC4cn+iXnA4KvcQYSV88vGn0Yi91vG47t1P7okprVmhNTkipNRIHWr6WdCO4VDr/cvsRkuVJAsLO2enwjGWWueOO6BodiBgyAOZ/5t5nJNMCNuLGT5UIo/RI1b0WRQwxEZTRjt6mFNw6lH14wRd8ulsr9toSWBPMOGWoYs1PDeDL0JuTjL+tr1SZi/EyxCngpYszKdXllJEHyI79KQgeD0Vt3pTrkbNVTOEcCNqZePSVmUH8X8Vhugz3bnE0/iE9Pb5fkWO9c4AnM1FgI/8Bvp27Fw2ShryIXuR6kKvUqhVMTuOSDHwu6A8jLE5Owt3GAYugDpDYuwTVNGrHLXKpPzrGGPE/jPmaLCMZcsdkec95dYeU3zKODEm8UQZFhmJmDeWVJ36nGrGZHL4J5aTTaeFUJmmXDaJYiJ+K2/ioKgXqnXvltu0A9R8/LGy4nrTJRr4JMLuJFoUXvGm1gXQ70w2LSpk6yl71RNC0hCtsBe8BP8IhYCM0EP5jh7eCMQZNvM= nocomment\n", IsWritable: true, } - DeployKeysPost(ctx, addKeyForm) + web.SetForm(ctx, &addKeyForm) + DeployKeysPost(ctx) assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) models.AssertExistsAndLoadBean(t, &models.DeployKey{ diff --git a/routers/repo/webhook.go b/routers/repo/webhook.go index 5d7074b339..01d142843f 100644 --- a/routers/repo/webhook.go +++ b/routers/repo/webhook.go @@ -13,14 +13,15 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/webhook" ) @@ -181,7 +182,8 @@ func ParseHookEvent(form auth.WebhookForm) *models.HookEvent { } // GiteaHooksNewPost response for creating Gitea webhook -func GiteaHooksNewPost(ctx *context.Context, form auth.NewWebhookForm) { +func GiteaHooksNewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewWebhookForm) ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksNew"] = true @@ -230,8 +232,9 @@ func GiteaHooksNewPost(ctx *context.Context, form auth.NewWebhookForm) { } // GogsHooksNewPost response for creating webhook -func GogsHooksNewPost(ctx *context.Context, form auth.NewGogshookForm) { - newGogsWebhookPost(ctx, form, models.GOGS) +func GogsHooksNewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewGogshookForm) + newGogsWebhookPost(ctx, *form, models.GOGS) } // newGogsWebhookPost response for creating gogs hook @@ -283,7 +286,8 @@ func newGogsWebhookPost(ctx *context.Context, form auth.NewGogshookForm, kind mo } // DiscordHooksNewPost response for creating discord hook -func DiscordHooksNewPost(ctx *context.Context, form auth.NewDiscordHookForm) { +func DiscordHooksNewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewDiscordHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksNew"] = true @@ -334,7 +338,8 @@ func DiscordHooksNewPost(ctx *context.Context, form auth.NewDiscordHookForm) { } // DingtalkHooksNewPost response for creating dingtalk hook -func DingtalkHooksNewPost(ctx *context.Context, form auth.NewDingtalkHookForm) { +func DingtalkHooksNewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewDingtalkHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksNew"] = true @@ -376,7 +381,8 @@ func DingtalkHooksNewPost(ctx *context.Context, form auth.NewDingtalkHookForm) { } // TelegramHooksNewPost response for creating telegram hook -func TelegramHooksNewPost(ctx *context.Context, form auth.NewTelegramHookForm) { +func TelegramHooksNewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewTelegramHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksNew"] = true @@ -427,7 +433,8 @@ func TelegramHooksNewPost(ctx *context.Context, form auth.NewTelegramHookForm) { } // MatrixHooksNewPost response for creating a Matrix hook -func MatrixHooksNewPost(ctx *context.Context, form auth.NewMatrixHookForm) { +func MatrixHooksNewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewMatrixHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksNew"] = true @@ -481,7 +488,8 @@ func MatrixHooksNewPost(ctx *context.Context, form auth.NewMatrixHookForm) { } // MSTeamsHooksNewPost response for creating MS Teams hook -func MSTeamsHooksNewPost(ctx *context.Context, form auth.NewMSTeamsHookForm) { +func MSTeamsHooksNewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewMSTeamsHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksNew"] = true @@ -523,7 +531,8 @@ func MSTeamsHooksNewPost(ctx *context.Context, form auth.NewMSTeamsHookForm) { } // SlackHooksNewPost response for creating slack hook -func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) { +func SlackHooksNewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewSlackHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksNew"] = true @@ -582,7 +591,8 @@ func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) { } // FeishuHooksNewPost response for creating feishu hook -func FeishuHooksNewPost(ctx *context.Context, form auth.NewFeishuHookForm) { +func FeishuHooksNewPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewFeishuHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksNew"] = true @@ -685,7 +695,8 @@ func WebHooksEdit(ctx *context.Context) { } // WebHooksEditPost response for editing web hook -func WebHooksEditPost(ctx *context.Context, form auth.NewWebhookForm) { +func WebHooksEditPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewWebhookForm) ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksEdit"] = true @@ -725,7 +736,8 @@ func WebHooksEditPost(ctx *context.Context, form auth.NewWebhookForm) { } // GogsHooksEditPost response for editing gogs hook -func GogsHooksEditPost(ctx *context.Context, form auth.NewGogshookForm) { +func GogsHooksEditPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewGogshookForm) ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksEdit"] = true @@ -764,7 +776,8 @@ func GogsHooksEditPost(ctx *context.Context, form auth.NewGogshookForm) { } // SlackHooksEditPost response for editing slack hook -func SlackHooksEditPost(ctx *context.Context, form auth.NewSlackHookForm) { +func SlackHooksEditPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewSlackHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksEdit"] = true @@ -814,7 +827,8 @@ func SlackHooksEditPost(ctx *context.Context, form auth.NewSlackHookForm) { } // DiscordHooksEditPost response for editing discord hook -func DiscordHooksEditPost(ctx *context.Context, form auth.NewDiscordHookForm) { +func DiscordHooksEditPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewDiscordHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksEdit"] = true @@ -856,7 +870,8 @@ func DiscordHooksEditPost(ctx *context.Context, form auth.NewDiscordHookForm) { } // DingtalkHooksEditPost response for editing discord hook -func DingtalkHooksEditPost(ctx *context.Context, form auth.NewDingtalkHookForm) { +func DingtalkHooksEditPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewDingtalkHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksEdit"] = true @@ -888,7 +903,8 @@ func DingtalkHooksEditPost(ctx *context.Context, form auth.NewDingtalkHookForm) } // TelegramHooksEditPost response for editing discord hook -func TelegramHooksEditPost(ctx *context.Context, form auth.NewTelegramHookForm) { +func TelegramHooksEditPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewTelegramHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksEdit"] = true @@ -928,7 +944,8 @@ func TelegramHooksEditPost(ctx *context.Context, form auth.NewTelegramHookForm) } // MatrixHooksEditPost response for editing a Matrix hook -func MatrixHooksEditPost(ctx *context.Context, form auth.NewMatrixHookForm) { +func MatrixHooksEditPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewMatrixHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksEdit"] = true @@ -971,7 +988,8 @@ func MatrixHooksEditPost(ctx *context.Context, form auth.NewMatrixHookForm) { } // MSTeamsHooksEditPost response for editing MS Teams hook -func MSTeamsHooksEditPost(ctx *context.Context, form auth.NewMSTeamsHookForm) { +func MSTeamsHooksEditPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewMSTeamsHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksEdit"] = true @@ -1003,7 +1021,8 @@ func MSTeamsHooksEditPost(ctx *context.Context, form auth.NewMSTeamsHookForm) { } // FeishuHooksEditPost response for editing feishu hook -func FeishuHooksEditPost(ctx *context.Context, form auth.NewFeishuHookForm) { +func FeishuHooksEditPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewFeishuHookForm) ctx.Data["Title"] = ctx.Tr("repo.settings") ctx.Data["PageIsSettingsHooks"] = true ctx.Data["PageIsSettingsHooksEdit"] = true diff --git a/routers/repo/wiki.go b/routers/repo/wiki.go index ac650d3fc4..c4521a3071 100644 --- a/routers/repo/wiki.go +++ b/routers/repo/wiki.go @@ -13,15 +13,16 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" wiki_service "code.gitea.io/gitea/services/wiki" ) @@ -556,7 +557,8 @@ func NewWiki(ctx *context.Context) { } // NewWikiPost response for wiki create request -func NewWikiPost(ctx *context.Context, form auth.NewWikiForm) { +func NewWikiPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewWikiForm) ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page") ctx.Data["PageIsWiki"] = true ctx.Data["RequireSimpleMDE"] = true @@ -613,7 +615,8 @@ func EditWiki(ctx *context.Context) { } // EditWikiPost response for wiki modify request -func EditWikiPost(ctx *context.Context, form auth.NewWikiForm) { +func EditWikiPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewWikiForm) ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page") ctx.Data["PageIsWiki"] = true ctx.Data["RequireSimpleMDE"] = true diff --git a/routers/repo/wiki_test.go b/routers/repo/wiki_test.go index cc79c808f5..badd07f080 100644 --- a/routers/repo/wiki_test.go +++ b/routers/repo/wiki_test.go @@ -10,9 +10,10 @@ import ( "testing" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/modules/web" wiki_service "code.gitea.io/gitea/services/wiki" "github.com/stretchr/testify/assert" @@ -114,11 +115,12 @@ func TestNewWikiPost(t *testing.T) { ctx := test.MockContext(t, "user2/repo1/wiki/_new") test.LoadUser(t, ctx, 2) test.LoadRepo(t, ctx, 1) - NewWikiPost(ctx, auth.NewWikiForm{ + web.SetForm(ctx, &auth.NewWikiForm{ Title: title, Content: content, Message: message, }) + NewWikiPost(ctx) assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) assertWikiExists(t, ctx.Repo.Repository, title) assert.Equal(t, wikiContent(t, ctx.Repo.Repository, title), content) @@ -131,11 +133,12 @@ func TestNewWikiPost_ReservedName(t *testing.T) { ctx := test.MockContext(t, "user2/repo1/wiki/_new") test.LoadUser(t, ctx, 2) test.LoadRepo(t, ctx, 1) - NewWikiPost(ctx, auth.NewWikiForm{ + web.SetForm(ctx, &auth.NewWikiForm{ Title: "_edit", Content: content, Message: message, }) + NewWikiPost(ctx) assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, ctx.Tr("repo.wiki.reserved_page"), ctx.Flash.ErrorMsg) assertWikiNotExists(t, ctx.Repo.Repository, "_edit") @@ -164,11 +167,12 @@ func TestEditWikiPost(t *testing.T) { ctx.SetParams(":page", "Home") test.LoadUser(t, ctx, 2) test.LoadRepo(t, ctx, 1) - EditWikiPost(ctx, auth.NewWikiForm{ + web.SetForm(ctx, &auth.NewWikiForm{ Title: title, Content: content, Message: message, }) + EditWikiPost(ctx) assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) assertWikiExists(t, ctx.Repo.Repository, title) assert.Equal(t, wikiContent(t, ctx.Repo.Repository, title), content) diff --git a/routers/routes/base.go b/routers/routes/base.go new file mode 100644 index 0000000000..a313032a88 --- /dev/null +++ b/routers/routes/base.go @@ -0,0 +1,246 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package routes + +import ( + "bytes" + "errors" + "fmt" + "io" + "net/http" + "os" + "path" + "strings" + "text/template" + "time" + + "code.gitea.io/gitea/modules/auth/sso" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/httpcache" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/middlewares" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/storage" + "code.gitea.io/gitea/modules/templates" + + "gitea.com/go-chi/session" +) + +type routerLoggerOptions struct { + req *http.Request + Identity *string + Start *time.Time + ResponseWriter http.ResponseWriter +} + +// SignedUserName returns signed user's name via context +func SignedUserName(req *http.Request) string { + ctx := context.GetContext(req) + if ctx != nil { + v := ctx.Data["SignedUserName"] + if res, ok := v.(string); ok { + return res + } + } + return "" +} + +func accessLogger() func(http.Handler) http.Handler { + logger := log.GetLogger("access") + logTemplate, _ := template.New("log").Parse(setting.AccessLogTemplate) + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + start := time.Now() + next.ServeHTTP(w, req) + identity := "-" + if val := SignedUserName(req); val != "" { + identity = val + } + rw := w + + buf := bytes.NewBuffer([]byte{}) + err := logTemplate.Execute(buf, routerLoggerOptions{ + req: req, + Identity: &identity, + Start: &start, + ResponseWriter: rw, + }) + if err != nil { + log.Error("Could not set up chi access logger: %v", err.Error()) + } + + err = logger.SendLog(log.INFO, "", "", 0, buf.String(), "") + if err != nil { + log.Error("Could not set up chi access logger: %v", err.Error()) + } + }) + } +} + +// LoggerHandler is a handler that will log the routing to the default gitea log +func LoggerHandler(level log.Level) func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + start := time.Now() + + _ = log.GetLogger("router").Log(0, level, "Started %s %s for %s", log.ColoredMethod(req.Method), req.URL.RequestURI(), req.RemoteAddr) + + next.ServeHTTP(w, req) + + var status int + if v, ok := w.(context.ResponseWriter); ok { + status = v.Status() + } + + _ = log.GetLogger("router").Log(0, level, "Completed %s %s %v %s in %v", log.ColoredMethod(req.Method), req.URL.RequestURI(), log.ColoredStatus(status), log.ColoredStatus(status, http.StatusText(status)), log.ColoredTime(time.Since(start))) + }) + } +} + +func storageHandler(storageSetting setting.Storage, prefix string, objStore storage.ObjectStorage) func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + if storageSetting.ServeDirect { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + if req.Method != "GET" && req.Method != "HEAD" { + next.ServeHTTP(w, req) + return + } + + if !strings.HasPrefix(req.URL.RequestURI(), "/"+prefix) { + next.ServeHTTP(w, req) + return + } + + rPath := strings.TrimPrefix(req.URL.RequestURI(), "/"+prefix) + u, err := objStore.URL(rPath, path.Base(rPath)) + if err != nil { + if os.IsNotExist(err) || errors.Is(err, os.ErrNotExist) { + log.Warn("Unable to find %s %s", prefix, rPath) + http.Error(w, "file not found", 404) + return + } + log.Error("Error whilst getting URL for %s %s. Error: %v", prefix, rPath, err) + http.Error(w, fmt.Sprintf("Error whilst getting URL for %s %s", prefix, rPath), 500) + return + } + http.Redirect( + w, + req, + u.String(), + 301, + ) + }) + } + + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + if req.Method != "GET" && req.Method != "HEAD" { + next.ServeHTTP(w, req) + return + } + + if !strings.HasPrefix(req.URL.RequestURI(), "/"+prefix) { + next.ServeHTTP(w, req) + return + } + + rPath := strings.TrimPrefix(req.URL.RequestURI(), "/"+prefix) + rPath = strings.TrimPrefix(rPath, "/") + + fi, err := objStore.Stat(rPath) + if err == nil && httpcache.HandleTimeCache(req, w, fi) { + return + } + + //If we have matched and access to release or issue + fr, err := objStore.Open(rPath) + if err != nil { + if os.IsNotExist(err) || errors.Is(err, os.ErrNotExist) { + log.Warn("Unable to find %s %s", prefix, rPath) + http.Error(w, "file not found", 404) + return + } + log.Error("Error whilst opening %s %s. Error: %v", prefix, rPath, err) + http.Error(w, fmt.Sprintf("Error whilst opening %s %s", prefix, rPath), 500) + return + } + defer fr.Close() + + _, err = io.Copy(w, fr) + if err != nil { + log.Error("Error whilst rendering %s %s. Error: %v", prefix, rPath, err) + http.Error(w, fmt.Sprintf("Error whilst rendering %s %s", prefix, rPath), 500) + return + } + }) + } +} + +type dataStore struct { + Data map[string]interface{} +} + +func (d *dataStore) GetData() map[string]interface{} { + return d.Data +} + +// Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so. +// This error will be created with the gitea 500 page. +func Recovery() func(next http.Handler) http.Handler { + var rnd = templates.HTMLRenderer() + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + defer func() { + if err := recover(); err != nil { + combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, string(log.Stack(2))) + log.Error("%v", combinedErr) + + sessionStore := session.GetSession(req) + if sessionStore == nil { + if setting.IsProd() { + http.Error(w, http.StatusText(500), 500) + } else { + http.Error(w, combinedErr, 500) + } + return + } + + var lc = middlewares.Locale(w, req) + var store = dataStore{ + Data: templates.Vars{ + "Language": lc.Language(), + "CurrentURL": setting.AppSubURL + req.URL.RequestURI(), + "i18n": lc, + }, + } + + // Get user from session if logged in. + user, _ := sso.SignedInUser(req, w, &store, sessionStore) + if user != nil { + store.Data["IsSigned"] = true + store.Data["SignedUser"] = user + store.Data["SignedUserID"] = user.ID + store.Data["SignedUserName"] = user.Name + store.Data["IsAdmin"] = user.IsAdmin + } else { + store.Data["SignedUserID"] = int64(0) + store.Data["SignedUserName"] = "" + } + + w.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) + + if !setting.IsProd() { + store.Data["ErrorMsg"] = combinedErr + } + err = rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store.Data)) + if err != nil { + log.Error("%v", err) + } + } + }() + + next.ServeHTTP(w, req) + }) + } +} diff --git a/routers/routes/chi.go b/routers/routes/chi.go deleted file mode 100644 index 2400140ae6..0000000000 --- a/routers/routes/chi.go +++ /dev/null @@ -1,309 +0,0 @@ -// Copyright 2020 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package routes - -import ( - "bytes" - "errors" - "fmt" - "io" - "net/http" - "os" - "path" - "strings" - "text/template" - "time" - - "code.gitea.io/gitea/modules/context" - "code.gitea.io/gitea/modules/httpcache" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/metrics" - "code.gitea.io/gitea/modules/public" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/storage" - "code.gitea.io/gitea/routers" - - "gitea.com/go-chi/session" - "github.com/go-chi/chi" - "github.com/go-chi/chi/middleware" - "github.com/prometheus/client_golang/prometheus" -) - -type routerLoggerOptions struct { - req *http.Request - Identity *string - Start *time.Time - ResponseWriter http.ResponseWriter -} - -// SignedUserName returns signed user's name via context -// FIXME currently no any data stored on chi.Context but macaron.Context, so this will -// return "" before we remove macaron totally -func SignedUserName(req *http.Request) string { - if v, ok := req.Context().Value("SignedUserName").(string); ok { - return v - } - return "" -} - -func setupAccessLogger(c chi.Router) { - logger := log.GetLogger("access") - - logTemplate, _ := template.New("log").Parse(setting.AccessLogTemplate) - c.Use(func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - start := time.Now() - next.ServeHTTP(w, req) - identity := "-" - if val := SignedUserName(req); val != "" { - identity = val - } - rw := w - - buf := bytes.NewBuffer([]byte{}) - err := logTemplate.Execute(buf, routerLoggerOptions{ - req: req, - Identity: &identity, - Start: &start, - ResponseWriter: rw, - }) - if err != nil { - log.Error("Could not set up macaron access logger: %v", err.Error()) - } - - err = logger.SendLog(log.INFO, "", "", 0, buf.String(), "") - if err != nil { - log.Error("Could not set up macaron access logger: %v", err.Error()) - } - }) - }) -} - -// LoggerHandler is a handler that will log the routing to the default gitea log -func LoggerHandler(level log.Level) func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - start := time.Now() - - _ = log.GetLogger("router").Log(0, level, "Started %s %s for %s", log.ColoredMethod(req.Method), req.URL.RequestURI(), req.RemoteAddr) - - next.ServeHTTP(w, req) - - var status int - if v, ok := w.(context.ResponseWriter); ok { - status = v.Status() - } - - _ = log.GetLogger("router").Log(0, level, "Completed %s %s %v %s in %v", log.ColoredMethod(req.Method), req.URL.RequestURI(), log.ColoredStatus(status), log.ColoredStatus(status, http.StatusText(status)), log.ColoredTime(time.Since(start))) - }) - } -} - -func storageHandler(storageSetting setting.Storage, prefix string, objStore storage.ObjectStorage) func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - if storageSetting.ServeDirect { - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - if req.Method != "GET" && req.Method != "HEAD" { - next.ServeHTTP(w, req) - return - } - - if !strings.HasPrefix(req.URL.RequestURI(), "/"+prefix) { - next.ServeHTTP(w, req) - return - } - - rPath := strings.TrimPrefix(req.URL.RequestURI(), "/"+prefix) - u, err := objStore.URL(rPath, path.Base(rPath)) - if err != nil { - if os.IsNotExist(err) || errors.Is(err, os.ErrNotExist) { - log.Warn("Unable to find %s %s", prefix, rPath) - http.Error(w, "file not found", 404) - return - } - log.Error("Error whilst getting URL for %s %s. Error: %v", prefix, rPath, err) - http.Error(w, fmt.Sprintf("Error whilst getting URL for %s %s", prefix, rPath), 500) - return - } - http.Redirect( - w, - req, - u.String(), - 301, - ) - }) - } - - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - if req.Method != "GET" && req.Method != "HEAD" { - next.ServeHTTP(w, req) - return - } - - if !strings.HasPrefix(req.URL.RequestURI(), "/"+prefix) { - next.ServeHTTP(w, req) - return - } - - rPath := strings.TrimPrefix(req.URL.RequestURI(), "/"+prefix) - rPath = strings.TrimPrefix(rPath, "/") - - fi, err := objStore.Stat(rPath) - if err == nil && httpcache.HandleTimeCache(req, w, fi) { - return - } - - //If we have matched and access to release or issue - fr, err := objStore.Open(rPath) - if err != nil { - if os.IsNotExist(err) || errors.Is(err, os.ErrNotExist) { - log.Warn("Unable to find %s %s", prefix, rPath) - http.Error(w, "file not found", 404) - return - } - log.Error("Error whilst opening %s %s. Error: %v", prefix, rPath, err) - http.Error(w, fmt.Sprintf("Error whilst opening %s %s", prefix, rPath), 500) - return - } - defer fr.Close() - - _, err = io.Copy(w, fr) - if err != nil { - log.Error("Error whilst rendering %s %s. Error: %v", prefix, rPath, err) - http.Error(w, fmt.Sprintf("Error whilst rendering %s %s", prefix, rPath), 500) - return - } - }) - } -} - -var ( - sessionManager *session.Manager -) - -// NewChi creates a chi Router -func NewChi() chi.Router { - c := chi.NewRouter() - c.Use(func(next http.Handler) http.Handler { - return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - next.ServeHTTP(context.NewResponse(resp), req) - }) - }) - c.Use(middleware.RealIP) - if !setting.DisableRouterLog && setting.RouterLogLevel != log.NONE { - if log.GetLogger("router").GetLevel() <= setting.RouterLogLevel { - c.Use(LoggerHandler(setting.RouterLogLevel)) - } - } - - var opt = session.Options{ - Provider: setting.SessionConfig.Provider, - ProviderConfig: setting.SessionConfig.ProviderConfig, - CookieName: setting.SessionConfig.CookieName, - CookiePath: setting.SessionConfig.CookiePath, - Gclifetime: setting.SessionConfig.Gclifetime, - Maxlifetime: setting.SessionConfig.Maxlifetime, - Secure: setting.SessionConfig.Secure, - Domain: setting.SessionConfig.Domain, - } - opt = session.PrepareOptions([]session.Options{opt}) - - var err error - sessionManager, err = session.NewManager(opt.Provider, opt) - if err != nil { - panic(err) - } - - c.Use(Recovery()) - if setting.EnableAccessLog { - setupAccessLogger(c) - } - - c.Use(public.Custom( - &public.Options{ - SkipLogging: setting.DisableRouterLog, - }, - )) - c.Use(public.Static( - &public.Options{ - Directory: path.Join(setting.StaticRootPath, "public"), - SkipLogging: setting.DisableRouterLog, - }, - )) - - c.Use(storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars)) - c.Use(storageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars)) - - return c -} - -// RegisterInstallRoute registers the install routes -func RegisterInstallRoute(c chi.Router) { - m := NewMacaron() - RegisterMacaronInstallRoute(m) - - // We need at least one handler in chi so that it does not drop - // our middleware: https://github.com/go-gitea/gitea/issues/13725#issuecomment-735244395 - c.Get("/", func(w http.ResponseWriter, req *http.Request) { - m.ServeHTTP(w, req) - }) - - c.NotFound(func(w http.ResponseWriter, req *http.Request) { - m.ServeHTTP(w, req) - }) - - c.MethodNotAllowed(func(w http.ResponseWriter, req *http.Request) { - m.ServeHTTP(w, req) - }) -} - -// NormalRoutes represents non install routes -func NormalRoutes() http.Handler { - r := chi.NewRouter() - - // for health check - r.Head("/", func(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(http.StatusOK) - }) - - if setting.HasRobotsTxt { - r.Get("/robots.txt", func(w http.ResponseWriter, req *http.Request) { - filePath := path.Join(setting.CustomPath, "robots.txt") - fi, err := os.Stat(filePath) - if err == nil && httpcache.HandleTimeCache(req, w, fi) { - return - } - http.ServeFile(w, req, filePath) - }) - } - - r.Get("/apple-touch-icon.png", func(w http.ResponseWriter, req *http.Request) { - http.Redirect(w, req, path.Join(setting.StaticURLPrefix, "img/apple-touch-icon.png"), 301) - }) - - // prometheus metrics endpoint - if setting.Metrics.Enabled { - c := metrics.NewCollector() - prometheus.MustRegister(c) - - r.Get("/metrics", routers.Metrics) - } - - return r -} - -// DelegateToMacaron delegates other routes to macaron -func DelegateToMacaron(r chi.Router) { - m := NewMacaron() - RegisterMacaronRoutes(m) - - r.NotFound(func(w http.ResponseWriter, req *http.Request) { - m.ServeHTTP(w, req) - }) - - r.MethodNotAllowed(func(w http.ResponseWriter, req *http.Request) { - m.ServeHTTP(w, req) - }) -} diff --git a/routers/routes/install.go b/routers/routes/install.go new file mode 100644 index 0000000000..0dc066d600 --- /dev/null +++ b/routers/routes/install.go @@ -0,0 +1,116 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package routes + +import ( + "fmt" + "net/http" + "path" + + "code.gitea.io/gitea/modules/forms" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/middlewares" + "code.gitea.io/gitea/modules/public" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/templates" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers" + + "gitea.com/go-chi/session" +) + +func installRecovery() func(next http.Handler) http.Handler { + var rnd = templates.HTMLRenderer() + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + defer func() { + // Why we need this? The first recover will try to render a beautiful + // error page for user, but the process can still panic again, then + // we have to just recover twice and send a simple error page that + // should not panic any more. + defer func() { + if err := recover(); err != nil { + combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, string(log.Stack(2))) + log.Error(combinedErr) + if setting.IsProd() { + http.Error(w, http.StatusText(500), 500) + } else { + http.Error(w, combinedErr, 500) + } + } + }() + + if err := recover(); err != nil { + combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, string(log.Stack(2))) + log.Error("%v", combinedErr) + + lc := middlewares.Locale(w, req) + var store = dataStore{ + Data: templates.Vars{ + "Language": lc.Language(), + "CurrentURL": setting.AppSubURL + req.URL.RequestURI(), + "i18n": lc, + "SignedUserID": int64(0), + "SignedUserName": "", + }, + } + + w.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) + + if !setting.IsProd() { + store.Data["ErrorMsg"] = combinedErr + } + err = rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store.Data)) + if err != nil { + log.Error("%v", err) + } + } + }() + + next.ServeHTTP(w, req) + }) + } +} + +// InstallRoutes registers the install routes +func InstallRoutes() *web.Route { + r := web.NewRoute() + for _, middle := range commonMiddlewares() { + r.Use(middle) + } + + r.Use(session.Sessioner(session.Options{ + Provider: setting.SessionConfig.Provider, + ProviderConfig: setting.SessionConfig.ProviderConfig, + CookieName: setting.SessionConfig.CookieName, + CookiePath: setting.SessionConfig.CookiePath, + Gclifetime: setting.SessionConfig.Gclifetime, + Maxlifetime: setting.SessionConfig.Maxlifetime, + Secure: setting.SessionConfig.Secure, + Domain: setting.SessionConfig.Domain, + })) + + r.Use(installRecovery()) + + r.Use(public.Custom( + &public.Options{ + SkipLogging: setting.DisableRouterLog, + }, + )) + r.Use(public.Static( + &public.Options{ + Directory: path.Join(setting.StaticRootPath, "public"), + SkipLogging: setting.DisableRouterLog, + }, + )) + + r.Use(routers.InstallInit) + r.Get("/", routers.Install) + r.Post("/", web.Bind(forms.InstallForm{}), routers.InstallPost) + r.NotFound(func(w http.ResponseWriter, req *http.Request) { + http.Redirect(w, req, setting.AppURL, 302) + }) + return r +} diff --git a/routers/routes/macaron.go b/routers/routes/macaron.go deleted file mode 100644 index f64a0a597b..0000000000 --- a/routers/routes/macaron.go +++ /dev/null @@ -1,993 +0,0 @@ -// Copyright 2017 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package routes - -import ( - "encoding/gob" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" - "code.gitea.io/gitea/modules/context" - "code.gitea.io/gitea/modules/lfs" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/options" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/templates" - "code.gitea.io/gitea/modules/validation" - "code.gitea.io/gitea/routers" - "code.gitea.io/gitea/routers/admin" - apiv1 "code.gitea.io/gitea/routers/api/v1" - "code.gitea.io/gitea/routers/dev" - "code.gitea.io/gitea/routers/events" - "code.gitea.io/gitea/routers/org" - "code.gitea.io/gitea/routers/private" - "code.gitea.io/gitea/routers/repo" - "code.gitea.io/gitea/routers/user" - userSetting "code.gitea.io/gitea/routers/user/setting" - "code.gitea.io/gitea/services/mailer" - - // to registers all internal adapters - _ "code.gitea.io/gitea/modules/session" - - "gitea.com/macaron/binding" - "gitea.com/macaron/cache" - "gitea.com/macaron/captcha" - "gitea.com/macaron/cors" - "gitea.com/macaron/csrf" - "gitea.com/macaron/gzip" - "gitea.com/macaron/i18n" - "gitea.com/macaron/macaron" - "gitea.com/macaron/session" - "gitea.com/macaron/toolbox" - "github.com/tstranex/u2f" -) - -// NewMacaron initializes Macaron instance. -func NewMacaron() *macaron.Macaron { - gob.Register(&u2f.Challenge{}) - var m *macaron.Macaron - if setting.RedirectMacaronLog { - loggerAsWriter := log.NewLoggerAsWriter("INFO", log.GetLogger("macaron")) - m = macaron.NewWithLogger(loggerAsWriter) - } else { - m = macaron.New() - } - - if setting.EnableGzip { - m.Use(gzip.Middleware()) - } - if setting.Protocol == setting.FCGI || setting.Protocol == setting.FCGIUnix { - m.SetURLPrefix(setting.AppSubURL) - } - - m.Use(templates.HTMLRenderer()) - - mailer.InitMailRender(templates.Mailer()) - - localeNames, err := options.Dir("locale") - - if err != nil { - log.Fatal("Failed to list locale files: %v", err) - } - - localFiles := make(map[string][]byte) - - for _, name := range localeNames { - localFiles[name], err = options.Locale(name) - - if err != nil { - log.Fatal("Failed to load %s locale file. %v", name, err) - } - } - - m.Use(i18n.I18n(i18n.Options{ - SubURL: setting.AppSubURL, - Files: localFiles, - Langs: setting.Langs, - Names: setting.Names, - DefaultLang: "en-US", - Redirect: false, - CookieHttpOnly: true, - Secure: setting.SessionConfig.Secure, - CookieDomain: setting.SessionConfig.Domain, - })) - m.Use(cache.Cacher(cache.Options{ - Adapter: setting.CacheService.Adapter, - AdapterConfig: setting.CacheService.Conn, - Interval: setting.CacheService.Interval, - })) - m.Use(captcha.Captchaer(captcha.Options{ - SubURL: setting.AppSubURL, - })) - m.Use(session.Sessioner(session.Options{ - Provider: setting.SessionConfig.Provider, - ProviderConfig: setting.SessionConfig.ProviderConfig, - CookieName: setting.SessionConfig.CookieName, - CookiePath: setting.SessionConfig.CookiePath, - Gclifetime: setting.SessionConfig.Gclifetime, - Maxlifetime: setting.SessionConfig.Maxlifetime, - Secure: setting.SessionConfig.Secure, - Domain: setting.SessionConfig.Domain, - })) - m.Use(csrf.Csrfer(csrf.Options{ - Secret: setting.SecretKey, - Cookie: setting.CSRFCookieName, - SetCookie: true, - Secure: setting.SessionConfig.Secure, - CookieHttpOnly: setting.CSRFCookieHTTPOnly, - Header: "X-Csrf-Token", - CookieDomain: setting.SessionConfig.Domain, - CookiePath: setting.AppSubURL, - })) - m.Use(toolbox.Toolboxer(m, toolbox.Options{ - HealthCheckFuncs: []*toolbox.HealthCheckFuncDesc{ - { - Desc: "Database connection", - Func: models.Ping, - }, - }, - DisableDebug: !setting.EnablePprof, - })) - m.Use(context.Contexter()) - m.SetAutoHead(true) - return m -} - -// RegisterMacaronInstallRoute registers the install routes -func RegisterMacaronInstallRoute(m *macaron.Macaron) { - m.Combo("/", routers.InstallInit).Get(routers.Install). - Post(binding.BindIgnErr(auth.InstallForm{}), routers.InstallPost) - m.NotFound(func(ctx *context.Context) { - ctx.Redirect(setting.AppURL, 302) - }) -} - -// RegisterMacaronRoutes routes routes to Macaron -func RegisterMacaronRoutes(m *macaron.Macaron) { - reqSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: true}) - ignSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: setting.Service.RequireSignInView}) - ignSignInAndCsrf := context.Toggle(&context.ToggleOptions{DisableCSRF: true}) - reqSignOut := context.Toggle(&context.ToggleOptions{SignOutRequired: true}) - - bindIgnErr := binding.BindIgnErr - validation.AddBindingRules() - - openIDSignInEnabled := func(ctx *context.Context) { - if !setting.Service.EnableOpenIDSignIn { - ctx.Error(403) - return - } - } - - openIDSignUpEnabled := func(ctx *context.Context) { - if !setting.Service.EnableOpenIDSignUp { - ctx.Error(403) - return - } - } - - reqMilestonesDashboardPageEnabled := func(ctx *context.Context) { - if !setting.Service.ShowMilestonesDashboardPage { - ctx.Error(403) - return - } - } - - m.Use(user.GetNotificationCount) - m.Use(repo.GetActiveStopwatch) - m.Use(func(ctx *context.Context) { - ctx.Data["UnitWikiGlobalDisabled"] = models.UnitTypeWiki.UnitGlobalDisabled() - ctx.Data["UnitIssuesGlobalDisabled"] = models.UnitTypeIssues.UnitGlobalDisabled() - ctx.Data["UnitPullsGlobalDisabled"] = models.UnitTypePullRequests.UnitGlobalDisabled() - ctx.Data["UnitProjectsGlobalDisabled"] = models.UnitTypeProjects.UnitGlobalDisabled() - }) - - // FIXME: not all routes need go through same middlewares. - // Especially some AJAX requests, we can reduce middleware number to improve performance. - // Routers. - // for health check - m.Get("/", routers.Home) - m.Group("/explore", func() { - m.Get("", func(ctx *context.Context) { - ctx.Redirect(setting.AppSubURL + "/explore/repos") - }) - m.Get("/repos", routers.ExploreRepos) - m.Get("/users", routers.ExploreUsers) - m.Get("/organizations", routers.ExploreOrganizations) - m.Get("/code", routers.ExploreCode) - }, ignSignIn) - m.Combo("/install", routers.InstallInit).Get(routers.Install). - Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost) - m.Get("/issues", reqSignIn, user.Issues) - m.Get("/pulls", reqSignIn, user.Pulls) - m.Get("/milestones", reqSignIn, reqMilestonesDashboardPageEnabled, user.Milestones) - - // ***** START: User ***** - m.Group("/user", func() { - m.Get("/login", user.SignIn) - m.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost) - m.Group("", func() { - m.Combo("/login/openid"). - Get(user.SignInOpenID). - Post(bindIgnErr(auth.SignInOpenIDForm{}), user.SignInOpenIDPost) - }, openIDSignInEnabled) - m.Group("/openid", func() { - m.Combo("/connect"). - Get(user.ConnectOpenID). - Post(bindIgnErr(auth.ConnectOpenIDForm{}), user.ConnectOpenIDPost) - m.Group("/register", func() { - m.Combo(""). - Get(user.RegisterOpenID, openIDSignUpEnabled). - Post(bindIgnErr(auth.SignUpOpenIDForm{}), user.RegisterOpenIDPost) - }, openIDSignUpEnabled) - }, openIDSignInEnabled) - m.Get("/sign_up", user.SignUp) - m.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost) - m.Group("/oauth2", func() { - m.Get("/:provider", user.SignInOAuth) - m.Get("/:provider/callback", user.SignInOAuthCallback) - }) - m.Get("/link_account", user.LinkAccount) - m.Post("/link_account_signin", bindIgnErr(auth.SignInForm{}), user.LinkAccountPostSignIn) - m.Post("/link_account_signup", bindIgnErr(auth.RegisterForm{}), user.LinkAccountPostRegister) - m.Group("/two_factor", func() { - m.Get("", user.TwoFactor) - m.Post("", bindIgnErr(auth.TwoFactorAuthForm{}), user.TwoFactorPost) - m.Get("/scratch", user.TwoFactorScratch) - m.Post("/scratch", bindIgnErr(auth.TwoFactorScratchAuthForm{}), user.TwoFactorScratchPost) - }) - m.Group("/u2f", func() { - m.Get("", user.U2F) - m.Get("/challenge", user.U2FChallenge) - m.Post("/sign", bindIgnErr(u2f.SignResponse{}), user.U2FSign) - - }) - }, reqSignOut) - - m.Any("/user/events", reqSignIn, events.Events) - - m.Group("/login/oauth", func() { - m.Get("/authorize", bindIgnErr(auth.AuthorizationForm{}), user.AuthorizeOAuth) - m.Post("/grant", bindIgnErr(auth.GrantApplicationForm{}), user.GrantApplicationOAuth) - // TODO manage redirection - m.Post("/authorize", bindIgnErr(auth.AuthorizationForm{}), user.AuthorizeOAuth) - }, ignSignInAndCsrf, reqSignIn) - m.Post("/login/oauth/access_token", bindIgnErr(auth.AccessTokenForm{}), ignSignInAndCsrf, user.AccessTokenOAuth) - - m.Group("/user/settings", func() { - m.Get("", userSetting.Profile) - m.Post("", bindIgnErr(auth.UpdateProfileForm{}), userSetting.ProfilePost) - m.Get("/change_password", user.MustChangePassword) - m.Post("/change_password", bindIgnErr(auth.MustChangePasswordForm{}), user.MustChangePasswordPost) - m.Post("/avatar", binding.MultipartForm(auth.AvatarForm{}), userSetting.AvatarPost) - m.Post("/avatar/delete", userSetting.DeleteAvatar) - m.Group("/account", func() { - m.Combo("").Get(userSetting.Account).Post(bindIgnErr(auth.ChangePasswordForm{}), userSetting.AccountPost) - m.Post("/email", bindIgnErr(auth.AddEmailForm{}), userSetting.EmailPost) - m.Post("/email/delete", userSetting.DeleteEmail) - m.Post("/delete", userSetting.DeleteAccount) - m.Post("/theme", bindIgnErr(auth.UpdateThemeForm{}), userSetting.UpdateUIThemePost) - }) - m.Group("/security", func() { - m.Get("", userSetting.Security) - m.Group("/two_factor", func() { - m.Post("/regenerate_scratch", userSetting.RegenerateScratchTwoFactor) - m.Post("/disable", userSetting.DisableTwoFactor) - m.Get("/enroll", userSetting.EnrollTwoFactor) - m.Post("/enroll", bindIgnErr(auth.TwoFactorAuthForm{}), userSetting.EnrollTwoFactorPost) - }) - m.Group("/u2f", func() { - m.Post("/request_register", bindIgnErr(auth.U2FRegistrationForm{}), userSetting.U2FRegister) - m.Post("/register", bindIgnErr(u2f.RegisterResponse{}), userSetting.U2FRegisterPost) - m.Post("/delete", bindIgnErr(auth.U2FDeleteForm{}), userSetting.U2FDelete) - }) - m.Group("/openid", func() { - m.Post("", bindIgnErr(auth.AddOpenIDForm{}), userSetting.OpenIDPost) - m.Post("/delete", userSetting.DeleteOpenID) - m.Post("/toggle_visibility", userSetting.ToggleOpenIDVisibility) - }, openIDSignInEnabled) - m.Post("/account_link", userSetting.DeleteAccountLink) - }) - m.Group("/applications/oauth2", func() { - m.Get("/:id", userSetting.OAuth2ApplicationShow) - m.Post("/:id", bindIgnErr(auth.EditOAuth2ApplicationForm{}), userSetting.OAuthApplicationsEdit) - m.Post("/:id/regenerate_secret", userSetting.OAuthApplicationsRegenerateSecret) - m.Post("", bindIgnErr(auth.EditOAuth2ApplicationForm{}), userSetting.OAuthApplicationsPost) - m.Post("/delete", userSetting.DeleteOAuth2Application) - m.Post("/revoke", userSetting.RevokeOAuth2Grant) - }) - m.Combo("/applications").Get(userSetting.Applications). - Post(bindIgnErr(auth.NewAccessTokenForm{}), userSetting.ApplicationsPost) - m.Post("/applications/delete", userSetting.DeleteApplication) - m.Combo("/keys").Get(userSetting.Keys). - Post(bindIgnErr(auth.AddKeyForm{}), userSetting.KeysPost) - m.Post("/keys/delete", userSetting.DeleteKey) - m.Get("/organization", userSetting.Organization) - m.Get("/repos", userSetting.Repos) - m.Post("/repos/unadopted", userSetting.AdoptOrDeleteRepository) - }, reqSignIn, func(ctx *context.Context) { - ctx.Data["PageIsUserSettings"] = true - ctx.Data["AllThemes"] = setting.UI.Themes - }) - - m.Group("/user", func() { - // r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) - m.Any("/activate", user.Activate, reqSignIn) - m.Any("/activate_email", user.ActivateEmail) - m.Get("/avatar/:username/:size", user.Avatar) - m.Get("/email2user", user.Email2User) - m.Get("/recover_account", user.ResetPasswd) - m.Post("/recover_account", user.ResetPasswdPost) - m.Get("/forgot_password", user.ForgotPasswd) - m.Post("/forgot_password", user.ForgotPasswdPost) - m.Post("/logout", user.SignOut) - m.Get("/task/:task", user.TaskStatus) - }) - // ***** END: User ***** - - m.Get("/avatar/:hash", user.AvatarByEmailHash) - - adminReq := context.Toggle(&context.ToggleOptions{SignInRequired: true, AdminRequired: true}) - - // ***** START: Admin ***** - m.Group("/admin", func() { - m.Get("", adminReq, admin.Dashboard) - m.Post("", adminReq, bindIgnErr(auth.AdminDashboardForm{}), admin.DashboardPost) - m.Get("/config", admin.Config) - m.Post("/config/test_mail", admin.SendTestMail) - m.Group("/monitor", func() { - m.Get("", admin.Monitor) - m.Post("/cancel/:pid", admin.MonitorCancel) - m.Group("/queue/:qid", func() { - m.Get("", admin.Queue) - m.Post("/set", admin.SetQueueSettings) - m.Post("/add", admin.AddWorkers) - m.Post("/cancel/:pid", admin.WorkerCancel) - m.Post("/flush", admin.Flush) - }) - }) - - m.Group("/users", func() { - m.Get("", admin.Users) - m.Combo("/new").Get(admin.NewUser).Post(bindIgnErr(auth.AdminCreateUserForm{}), admin.NewUserPost) - m.Combo("/:userid").Get(admin.EditUser).Post(bindIgnErr(auth.AdminEditUserForm{}), admin.EditUserPost) - m.Post("/:userid/delete", admin.DeleteUser) - }) - - m.Group("/emails", func() { - m.Get("", admin.Emails) - m.Post("/activate", admin.ActivateEmail) - }) - - m.Group("/orgs", func() { - m.Get("", admin.Organizations) - }) - - m.Group("/repos", func() { - m.Get("", admin.Repos) - m.Combo("/unadopted").Get(admin.UnadoptedRepos).Post(admin.AdoptOrDeleteRepository) - m.Post("/delete", admin.DeleteRepo) - }) - - m.Group("/hooks", func() { - m.Get("", admin.DefaultOrSystemWebhooks) - m.Post("/delete", admin.DeleteDefaultOrSystemWebhook) - m.Get("/:id", repo.WebHooksEdit) - m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) - m.Post("/gogs/:id", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost) - m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) - m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) - m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) - m.Post("/telegram/:id", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost) - m.Post("/matrix/:id", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksEditPost) - m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) - m.Post("/feishu/:id", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksEditPost) - }) - - m.Group("/^:configType(default-hooks|system-hooks)$", func() { - m.Get("/:type/new", repo.WebhooksNew) - m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.GiteaHooksNewPost) - m.Post("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost) - m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) - m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost) - m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost) - m.Post("/telegram/new", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksNewPost) - m.Post("/matrix/new", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksNewPost) - m.Post("/msteams/new", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost) - m.Post("/feishu/new", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksNewPost) - }) - - m.Group("/auths", func() { - m.Get("", admin.Authentications) - m.Combo("/new").Get(admin.NewAuthSource).Post(bindIgnErr(auth.AuthenticationForm{}), admin.NewAuthSourcePost) - m.Combo("/:authid").Get(admin.EditAuthSource). - Post(bindIgnErr(auth.AuthenticationForm{}), admin.EditAuthSourcePost) - m.Post("/:authid/delete", admin.DeleteAuthSource) - }) - - m.Group("/notices", func() { - m.Get("", admin.Notices) - m.Post("/delete", admin.DeleteNotices) - m.Post("/empty", admin.EmptyNotices) - }) - }, adminReq) - // ***** END: Admin ***** - - m.Group("", func() { - m.Get("/:username", user.Profile) - m.Get("/attachments/:uuid", repo.GetAttachment) - }, ignSignIn) - - m.Group("/:username", func() { - m.Post("/action/:action", user.Action) - }, reqSignIn) - - if macaron.Env == macaron.DEV { - m.Get("/template/*", dev.TemplatePreview) - } - - reqRepoAdmin := context.RequireRepoAdmin() - reqRepoCodeWriter := context.RequireRepoWriter(models.UnitTypeCode) - reqRepoCodeReader := context.RequireRepoReader(models.UnitTypeCode) - reqRepoReleaseWriter := context.RequireRepoWriter(models.UnitTypeReleases) - reqRepoReleaseReader := context.RequireRepoReader(models.UnitTypeReleases) - reqRepoWikiWriter := context.RequireRepoWriter(models.UnitTypeWiki) - reqRepoIssueWriter := context.RequireRepoWriter(models.UnitTypeIssues) - reqRepoIssueReader := context.RequireRepoReader(models.UnitTypeIssues) - reqRepoPullsReader := context.RequireRepoReader(models.UnitTypePullRequests) - reqRepoIssuesOrPullsWriter := context.RequireRepoWriterOr(models.UnitTypeIssues, models.UnitTypePullRequests) - reqRepoIssuesOrPullsReader := context.RequireRepoReaderOr(models.UnitTypeIssues, models.UnitTypePullRequests) - reqRepoProjectsReader := context.RequireRepoReader(models.UnitTypeProjects) - reqRepoProjectsWriter := context.RequireRepoWriter(models.UnitTypeProjects) - - // ***** START: Organization ***** - m.Group("/org", func() { - m.Group("", func() { - m.Get("/create", org.Create) - m.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.CreatePost) - }) - - m.Group("/:org", func() { - m.Get("/dashboard", user.Dashboard) - m.Get("/dashboard/:team", user.Dashboard) - m.Get("/issues", user.Issues) - m.Get("/issues/:team", user.Issues) - m.Get("/pulls", user.Pulls) - m.Get("/pulls/:team", user.Pulls) - m.Get("/milestones", reqMilestonesDashboardPageEnabled, user.Milestones) - m.Get("/milestones/:team", reqMilestonesDashboardPageEnabled, user.Milestones) - m.Get("/members", org.Members) - m.Post("/members/action/:action", org.MembersAction) - m.Get("/teams", org.Teams) - }, context.OrgAssignment(true, false, true)) - - m.Group("/:org", func() { - m.Get("/teams/:team", org.TeamMembers) - m.Get("/teams/:team/repositories", org.TeamRepositories) - m.Post("/teams/:team/action/:action", org.TeamsAction) - m.Post("/teams/:team/action/repo/:action", org.TeamsRepoAction) - }, context.OrgAssignment(true, false, true)) - - m.Group("/:org", func() { - m.Get("/teams/new", org.NewTeam) - m.Post("/teams/new", bindIgnErr(auth.CreateTeamForm{}), org.NewTeamPost) - m.Get("/teams/:team/edit", org.EditTeam) - m.Post("/teams/:team/edit", bindIgnErr(auth.CreateTeamForm{}), org.EditTeamPost) - m.Post("/teams/:team/delete", org.DeleteTeam) - - m.Group("/settings", func() { - m.Combo("").Get(org.Settings). - Post(bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost) - m.Post("/avatar", binding.MultipartForm(auth.AvatarForm{}), org.SettingsAvatar) - m.Post("/avatar/delete", org.SettingsDeleteAvatar) - - m.Group("/hooks", func() { - m.Get("", org.Webhooks) - m.Post("/delete", org.DeleteWebhook) - m.Get("/:type/new", repo.WebhooksNew) - m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.GiteaHooksNewPost) - m.Post("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost) - m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) - m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost) - m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost) - m.Post("/telegram/new", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksNewPost) - m.Post("/matrix/new", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksNewPost) - m.Post("/msteams/new", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost) - m.Post("/feishu/new", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksNewPost) - m.Get("/:id", repo.WebHooksEdit) - m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) - m.Post("/gogs/:id", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost) - m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) - m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) - m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) - m.Post("/telegram/:id", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost) - m.Post("/matrix/:id", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksEditPost) - m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) - m.Post("/feishu/:id", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksEditPost) - }) - - m.Group("/labels", func() { - m.Get("", org.RetrieveLabels, org.Labels) - m.Post("/new", bindIgnErr(auth.CreateLabelForm{}), org.NewLabel) - m.Post("/edit", bindIgnErr(auth.CreateLabelForm{}), org.UpdateLabel) - m.Post("/delete", org.DeleteLabel) - m.Post("/initialize", bindIgnErr(auth.InitializeLabelsForm{}), org.InitializeLabels) - }) - - m.Route("/delete", "GET,POST", org.SettingsDelete) - }) - }, context.OrgAssignment(true, true)) - }, reqSignIn) - // ***** END: Organization ***** - - // ***** START: Repository ***** - m.Group("/repo", func() { - m.Get("/create", repo.Create) - m.Post("/create", bindIgnErr(auth.CreateRepoForm{}), repo.CreatePost) - m.Get("/migrate", repo.Migrate) - m.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), repo.MigratePost) - m.Group("/fork", func() { - m.Combo("/:repoid").Get(repo.Fork). - Post(bindIgnErr(auth.CreateRepoForm{}), repo.ForkPost) - }, context.RepoIDAssignment(), context.UnitTypes(), reqRepoCodeReader) - }, reqSignIn) - - // ***** Release Attachment Download without Signin - m.Get("/:username/:reponame/releases/download/:vTag/:fileName", ignSignIn, context.RepoAssignment(), repo.MustBeNotEmpty, repo.RedirectDownload) - - m.Group("/:username/:reponame", func() { - m.Group("/settings", func() { - m.Combo("").Get(repo.Settings). - Post(bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost) - m.Post("/avatar", binding.MultipartForm(auth.AvatarForm{}), repo.SettingsAvatar) - m.Post("/avatar/delete", repo.SettingsDeleteAvatar) - - m.Group("/collaboration", func() { - m.Combo("").Get(repo.Collaboration).Post(repo.CollaborationPost) - m.Post("/access_mode", repo.ChangeCollaborationAccessMode) - m.Post("/delete", repo.DeleteCollaboration) - m.Group("/team", func() { - m.Post("", repo.AddTeamPost) - m.Post("/delete", repo.DeleteTeam) - }) - }) - m.Group("/branches", func() { - m.Combo("").Get(repo.ProtectedBranch).Post(repo.ProtectedBranchPost) - m.Combo("/*").Get(repo.SettingsProtectedBranch). - Post(bindIgnErr(auth.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo.SettingsProtectedBranchPost) - }, repo.MustBeNotEmpty) - - m.Group("/hooks", func() { - m.Get("", repo.Webhooks) - m.Post("/delete", repo.DeleteWebhook) - m.Get("/:type/new", repo.WebhooksNew) - m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.GiteaHooksNewPost) - m.Post("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost) - m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) - m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost) - m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost) - m.Post("/telegram/new", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksNewPost) - m.Post("/matrix/new", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksNewPost) - m.Post("/msteams/new", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost) - m.Post("/feishu/new", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksNewPost) - m.Get("/:id", repo.WebHooksEdit) - m.Post("/:id/test", repo.TestWebhook) - m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) - m.Post("/gogs/:id", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost) - m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) - m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) - m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) - m.Post("/telegram/:id", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost) - m.Post("/matrix/:id", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksEditPost) - m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) - m.Post("/feishu/:id", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksEditPost) - - m.Group("/git", func() { - m.Get("", repo.GitHooks) - m.Combo("/:name").Get(repo.GitHooksEdit). - Post(repo.GitHooksEditPost) - }, context.GitHookService()) - }) - - m.Group("/keys", func() { - m.Combo("").Get(repo.DeployKeys). - Post(bindIgnErr(auth.AddKeyForm{}), repo.DeployKeysPost) - m.Post("/delete", repo.DeleteDeployKey) - }) - - m.Group("/lfs", func() { - m.Get("", repo.LFSFiles) - m.Get("/show/:oid", repo.LFSFileGet) - m.Post("/delete/:oid", repo.LFSDelete) - m.Get("/pointers", repo.LFSPointerFiles) - m.Post("/pointers/associate", repo.LFSAutoAssociate) - m.Get("/find", repo.LFSFileFind) - m.Group("/locks", func() { - m.Get("/", repo.LFSLocks) - m.Post("/", repo.LFSLockFile) - m.Post("/:lid/unlock", repo.LFSUnlock) - }) - }) - - }, func(ctx *context.Context) { - ctx.Data["PageIsSettings"] = true - ctx.Data["LFSStartServer"] = setting.LFS.StartServer - }) - }, reqSignIn, context.RepoAssignment(), context.UnitTypes(), reqRepoAdmin, context.RepoRef()) - - m.Post("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.UnitTypes(), repo.Action) - - // Grouping for those endpoints not requiring authentication - m.Group("/:username/:reponame", func() { - m.Group("/milestone", func() { - m.Get("/:id", repo.MilestoneIssuesAndPulls) - }, reqRepoIssuesOrPullsReader, context.RepoRef()) - m.Combo("/compare/*", repo.MustBeNotEmpty, reqRepoCodeReader, repo.SetEditorconfigIfExists). - Get(ignSignIn, repo.SetDiffViewStyle, repo.CompareDiff). - Post(reqSignIn, context.RepoMustNotBeArchived(), reqRepoPullsReader, repo.MustAllowPulls, bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost) - }, context.RepoAssignment(), context.UnitTypes()) - - // Grouping for those endpoints that do require authentication - m.Group("/:username/:reponame", func() { - m.Group("/issues", func() { - m.Group("/new", func() { - m.Combo("").Get(context.RepoRef(), repo.NewIssue). - Post(bindIgnErr(auth.CreateIssueForm{}), repo.NewIssuePost) - m.Get("/choose", context.RepoRef(), repo.NewIssueChooseTemplate) - }) - }, context.RepoMustNotBeArchived(), reqRepoIssueReader) - // FIXME: should use different URLs but mostly same logic for comments of issue and pull reuqest. - // So they can apply their own enable/disable logic on routers. - m.Group("/issues", func() { - m.Group("/:index", func() { - m.Post("/title", repo.UpdateIssueTitle) - m.Post("/content", repo.UpdateIssueContent) - m.Post("/watch", repo.IssueWatch) - m.Post("/ref", repo.UpdateIssueRef) - m.Group("/dependency", func() { - m.Post("/add", repo.AddDependency) - m.Post("/delete", repo.RemoveDependency) - }) - m.Combo("/comments").Post(repo.MustAllowUserComment, bindIgnErr(auth.CreateCommentForm{}), repo.NewComment) - m.Group("/times", func() { - m.Post("/add", bindIgnErr(auth.AddTimeManuallyForm{}), repo.AddTimeManually) - m.Group("/stopwatch", func() { - m.Post("/toggle", repo.IssueStopwatch) - m.Post("/cancel", repo.CancelStopwatch) - }) - }) - m.Post("/reactions/:action", bindIgnErr(auth.ReactionForm{}), repo.ChangeIssueReaction) - m.Post("/lock", reqRepoIssueWriter, bindIgnErr(auth.IssueLockForm{}), repo.LockIssue) - m.Post("/unlock", reqRepoIssueWriter, repo.UnlockIssue) - }, context.RepoMustNotBeArchived()) - m.Group("/:index", func() { - m.Get("/attachments", repo.GetIssueAttachments) - m.Get("/attachments/:uuid", repo.GetAttachment) - }) - - m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel) - m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone) - m.Post("/projects", reqRepoIssuesOrPullsWriter, repo.UpdateIssueProject) - m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee) - m.Post("/request_review", reqRepoIssuesOrPullsReader, repo.UpdatePullReviewRequest) - m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus) - m.Post("/resolve_conversation", reqRepoIssuesOrPullsReader, repo.UpdateResolveConversation) - m.Post("/attachments", repo.UploadIssueAttachment) - m.Post("/attachments/remove", repo.DeleteAttachment) - }, context.RepoMustNotBeArchived()) - m.Group("/comments/:id", func() { - m.Post("", repo.UpdateCommentContent) - m.Post("/delete", repo.DeleteComment) - m.Post("/reactions/:action", bindIgnErr(auth.ReactionForm{}), repo.ChangeCommentReaction) - }, context.RepoMustNotBeArchived()) - m.Group("/comments/:id", func() { - m.Get("/attachments", repo.GetCommentAttachments) - }) - m.Group("/labels", func() { - m.Post("/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel) - m.Post("/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel) - m.Post("/delete", repo.DeleteLabel) - m.Post("/initialize", bindIgnErr(auth.InitializeLabelsForm{}), repo.InitializeLabels) - }, context.RepoMustNotBeArchived(), reqRepoIssuesOrPullsWriter, context.RepoRef()) - m.Group("/milestones", func() { - m.Combo("/new").Get(repo.NewMilestone). - Post(bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost) - m.Get("/:id/edit", repo.EditMilestone) - m.Post("/:id/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.EditMilestonePost) - m.Post("/:id/:action", repo.ChangeMilestoneStatus) - m.Post("/delete", repo.DeleteMilestone) - }, context.RepoMustNotBeArchived(), reqRepoIssuesOrPullsWriter, context.RepoRef()) - m.Group("/pull", func() { - m.Post("/:index/target_branch", repo.UpdatePullRequestTarget) - }, context.RepoMustNotBeArchived()) - - m.Group("", func() { - m.Group("", func() { - m.Combo("/_edit/*").Get(repo.EditFile). - Post(bindIgnErr(auth.EditRepoFileForm{}), repo.EditFilePost) - m.Combo("/_new/*").Get(repo.NewFile). - Post(bindIgnErr(auth.EditRepoFileForm{}), repo.NewFilePost) - m.Post("/_preview/*", bindIgnErr(auth.EditPreviewDiffForm{}), repo.DiffPreviewPost) - m.Combo("/_delete/*").Get(repo.DeleteFile). - Post(bindIgnErr(auth.DeleteRepoFileForm{}), repo.DeleteFilePost) - m.Combo("/_upload/*", repo.MustBeAbleToUpload). - Get(repo.UploadFile). - Post(bindIgnErr(auth.UploadRepoFileForm{}), repo.UploadFilePost) - }, context.RepoRefByType(context.RepoRefBranch), repo.MustBeEditable) - m.Group("", func() { - m.Post("/upload-file", repo.UploadFileToServer) - m.Post("/upload-remove", bindIgnErr(auth.RemoveUploadFileForm{}), repo.RemoveUploadFileFromServer) - }, context.RepoRef(), repo.MustBeEditable, repo.MustBeAbleToUpload) - }, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty) - - m.Group("/branches", func() { - m.Group("/_new/", func() { - m.Post("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.CreateBranch) - m.Post("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.CreateBranch) - m.Post("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.CreateBranch) - }, bindIgnErr(auth.NewBranchForm{})) - m.Post("/delete", repo.DeleteBranchPost) - m.Post("/restore", repo.RestoreBranchPost) - }, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty) - - }, reqSignIn, context.RepoAssignment(), context.UnitTypes()) - - // Releases - m.Group("/:username/:reponame", func() { - m.Get("/tags", repo.TagsList, repo.MustBeNotEmpty, - reqRepoCodeReader, context.RepoRefByType(context.RepoRefTag)) - m.Group("/releases", func() { - m.Get("/", repo.Releases) - m.Get("/tag/*", repo.SingleRelease) - m.Get("/latest", repo.LatestRelease) - m.Get("/attachments/:uuid", repo.GetAttachment) - }, repo.MustBeNotEmpty, reqRepoReleaseReader, context.RepoRefByType(context.RepoRefTag)) - m.Group("/releases", func() { - m.Get("/new", repo.NewRelease) - m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost) - m.Post("/delete", repo.DeleteRelease) - m.Post("/attachments", repo.UploadReleaseAttachment) - m.Post("/attachments/remove", repo.DeleteAttachment) - }, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, context.RepoRef()) - m.Post("/tags/delete", repo.DeleteTag, reqSignIn, - repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoCodeWriter, context.RepoRef()) - m.Group("/releases", func() { - m.Get("/edit/*", repo.EditRelease) - m.Post("/edit/*", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost) - }, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, func(ctx *context.Context) { - var err error - ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch) - if err != nil { - ctx.ServerError("GetBranchCommit", err) - return - } - ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount() - if err != nil { - ctx.ServerError("GetCommitsCount", err) - return - } - ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount - }) - }, ignSignIn, context.RepoAssignment(), context.UnitTypes(), reqRepoReleaseReader) - - m.Group("/:username/:reponame", func() { - m.Post("/topics", repo.TopicsPost) - }, context.RepoAssignment(), context.RepoMustNotBeArchived(), reqRepoAdmin) - - m.Group("/:username/:reponame", func() { - m.Group("", func() { - m.Get("/^:type(issues|pulls)$", repo.Issues) - m.Get("/^:type(issues|pulls)$/:index", repo.ViewIssue) - m.Get("/labels/", reqRepoIssuesOrPullsReader, repo.RetrieveLabels, repo.Labels) - m.Get("/milestones", reqRepoIssuesOrPullsReader, repo.Milestones) - }, context.RepoRef()) - - m.Group("/projects", func() { - m.Get("", repo.Projects) - m.Get("/:id", repo.ViewProject) - m.Group("", func() { - m.Get("/new", repo.NewProject) - m.Post("/new", bindIgnErr(auth.CreateProjectForm{}), repo.NewProjectPost) - m.Group("/:id", func() { - m.Post("", bindIgnErr(auth.EditProjectBoardTitleForm{}), repo.AddBoardToProjectPost) - m.Post("/delete", repo.DeleteProject) - - m.Get("/edit", repo.EditProject) - m.Post("/edit", bindIgnErr(auth.CreateProjectForm{}), repo.EditProjectPost) - m.Post("/^:action(open|close)$", repo.ChangeProjectStatus) - - m.Group("/:boardID", func() { - m.Put("", bindIgnErr(auth.EditProjectBoardTitleForm{}), repo.EditProjectBoardTitle) - m.Delete("", repo.DeleteProjectBoard) - m.Post("/default", repo.SetDefaultProjectBoard) - - m.Post("/:index", repo.MoveIssueAcrossBoards) - }) - }) - }, reqRepoProjectsWriter, context.RepoMustNotBeArchived()) - }, reqRepoProjectsReader, repo.MustEnableProjects) - - m.Group("/wiki", func() { - m.Get("/?:page", repo.Wiki) - m.Get("/_pages", repo.WikiPages) - m.Get("/:page/_revision", repo.WikiRevision) - m.Get("/commit/:sha([a-f0-9]{7,40})$", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff) - m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", repo.RawDiff) - - m.Group("", func() { - m.Combo("/_new").Get(repo.NewWiki). - Post(bindIgnErr(auth.NewWikiForm{}), repo.NewWikiPost) - m.Combo("/:page/_edit").Get(repo.EditWiki). - Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost) - m.Post("/:page/delete", repo.DeleteWikiPagePost) - }, context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter) - }, repo.MustEnableWiki, context.RepoRef(), func(ctx *context.Context) { - ctx.Data["PageIsWiki"] = true - }) - - m.Group("/wiki", func() { - m.Get("/raw/*", repo.WikiRaw) - }, repo.MustEnableWiki) - - m.Group("/activity", func() { - m.Get("", repo.Activity) - m.Get("/:period", repo.Activity) - }, context.RepoRef(), repo.MustBeNotEmpty, context.RequireRepoReaderOr(models.UnitTypePullRequests, models.UnitTypeIssues, models.UnitTypeReleases)) - - m.Group("/activity_author_data", func() { - m.Get("", repo.ActivityAuthors) - m.Get("/:period", repo.ActivityAuthors) - }, context.RepoRef(), repo.MustBeNotEmpty, context.RequireRepoReaderOr(models.UnitTypeCode)) - - m.Group("/archive", func() { - m.Get("/*", repo.Download) - m.Post("/*", repo.InitiateDownload) - }, repo.MustBeNotEmpty, reqRepoCodeReader) - - m.Group("/branches", func() { - m.Get("", repo.Branches) - }, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) - - m.Group("/blob_excerpt", func() { - m.Get("/:sha", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ExcerptBlob) - }, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) - - m.Group("/pulls/:index", func() { - m.Get(".diff", repo.DownloadPullDiff) - m.Get(".patch", repo.DownloadPullPatch) - m.Get("/commits", context.RepoRef(), repo.ViewPullCommits) - m.Post("/merge", context.RepoMustNotBeArchived(), bindIgnErr(auth.MergePullRequestForm{}), repo.MergePullRequest) - m.Post("/update", repo.UpdatePullRequest) - m.Post("/cleanup", context.RepoMustNotBeArchived(), context.RepoRef(), repo.CleanUpPullRequest) - m.Group("/files", func() { - m.Get("", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.ViewPullFiles) - m.Group("/reviews", func() { - m.Get("/new_comment", repo.RenderNewCodeCommentForm) - m.Post("/comments", bindIgnErr(auth.CodeCommentForm{}), repo.CreateCodeComment) - m.Post("/submit", bindIgnErr(auth.SubmitReviewForm{}), repo.SubmitReview) - }, context.RepoMustNotBeArchived()) - }) - }, repo.MustAllowPulls) - - m.Group("/media", func() { - m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.SingleDownloadOrLFS) - m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.SingleDownloadOrLFS) - m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.SingleDownloadOrLFS) - m.Get("/blob/:sha", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByIDOrLFS) - // "/*" route is deprecated, and kept for backward compatibility - m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownloadOrLFS) - }, repo.MustBeNotEmpty, reqRepoCodeReader) - - m.Group("/raw", func() { - m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.SingleDownload) - m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.SingleDownload) - m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.SingleDownload) - m.Get("/blob/:sha", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByID) - // "/*" route is deprecated, and kept for backward compatibility - m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownload) - }, repo.MustBeNotEmpty, reqRepoCodeReader) - - m.Group("/commits", func() { - m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefCommits) - m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RefCommits) - m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RefCommits) - // "/*" route is deprecated, and kept for backward compatibility - m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.RefCommits) - }, repo.MustBeNotEmpty, reqRepoCodeReader) - - m.Group("/blame", func() { - m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefBlame) - m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RefBlame) - m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RefBlame) - }, repo.MustBeNotEmpty, reqRepoCodeReader) - - m.Group("", func() { - m.Get("/graph", repo.Graph) - m.Get("/commit/:sha([a-f0-9]{7,40})$", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff) - }, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) - - m.Group("/src", func() { - m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home) - m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.Home) - m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.Home) - // "/*" route is deprecated, and kept for backward compatibility - m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.Home) - }, repo.SetEditorconfigIfExists) - - m.Group("", func() { - m.Get("/forks", repo.Forks) - }, context.RepoRef(), reqRepoCodeReader) - m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", - repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff) - }, ignSignIn, context.RepoAssignment(), context.UnitTypes()) - m.Group("/:username/:reponame", func() { - m.Get("/stars", repo.Stars) - m.Get("/watchers", repo.Watchers) - m.Get("/search", reqRepoCodeReader, repo.Search) - }, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes()) - - m.Group("/:username", func() { - m.Group("/:reponame", func() { - m.Get("", repo.SetEditorconfigIfExists, repo.Home) - m.Get("\\.git$", repo.SetEditorconfigIfExists, repo.Home) - }, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes()) - - m.Group("/:reponame", func() { - m.Group("\\.git/info/lfs", func() { - m.Post("/objects/batch", lfs.BatchHandler) - m.Get("/objects/:oid/:filename", lfs.ObjectOidHandler) - m.Any("/objects/:oid", lfs.ObjectOidHandler) - m.Post("/objects", lfs.PostHandler) - m.Post("/verify", lfs.VerifyHandler) - m.Group("/locks", func() { - m.Get("/", lfs.GetListLockHandler) - m.Post("/", lfs.PostLockHandler) - m.Post("/verify", lfs.VerifyLockHandler) - m.Post("/:lid/unlock", lfs.UnLockHandler) - }) - m.Any("/*", func(ctx *context.Context) { - ctx.NotFound("", nil) - }) - }, ignSignInAndCsrf) - m.Any("/*", ignSignInAndCsrf, repo.HTTP) - m.Head("/tasks/trigger", repo.TriggerTask) - }) - }) - // ***** END: Repository ***** - - m.Group("/notifications", func() { - m.Get("", user.Notifications) - m.Post("/status", user.NotificationStatusPost) - m.Post("/purge", user.NotificationPurgePost) - }, reqSignIn) - - if setting.API.EnableSwagger { - m.Get("/swagger.v1.json", templates.JSONRenderer(), routers.SwaggerV1Json) - } - - var handlers []macaron.Handler - if setting.CORSConfig.Enabled { - handlers = append(handlers, cors.CORS(cors.Options{ - Scheme: setting.CORSConfig.Scheme, - AllowDomain: setting.CORSConfig.AllowDomain, - AllowSubdomain: setting.CORSConfig.AllowSubdomain, - Methods: setting.CORSConfig.Methods, - MaxAgeSeconds: int(setting.CORSConfig.MaxAge.Seconds()), - AllowCredentials: setting.CORSConfig.AllowCredentials, - })) - } - handlers = append(handlers, ignSignIn) - m.Group("/api", func() { - apiv1.RegisterRoutes(m) - }, handlers...) - - m.Group("/api/internal", func() { - // package name internal is ideal but Golang is not allowed, so we use private as package name. - private.RegisterRoutes(m) - }) - - // Not found handler. - m.NotFound(routers.NotFound) -} diff --git a/routers/routes/recovery.go b/routers/routes/recovery.go deleted file mode 100644 index cfe1a4114c..0000000000 --- a/routers/routes/recovery.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2020 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package routes - -import ( - "fmt" - "net/http" - - "code.gitea.io/gitea/modules/auth/sso" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/middlewares" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/templates" - - "github.com/unrolled/render" -) - -type dataStore struct { - Data map[string]interface{} -} - -func (d *dataStore) GetData() map[string]interface{} { - return d.Data -} - -// Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so. -// Although similar to macaron.Recovery() the main difference is that this error will be created -// with the gitea 500 page. -func Recovery() func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - rnd := render.New(render.Options{ - Extensions: []string{".tmpl"}, - Directory: "templates", - Funcs: templates.NewFuncMap(), - Asset: templates.GetAsset, - AssetNames: templates.GetAssetNames, - IsDevelopment: !setting.IsProd(), - }) - - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - defer func() { - // Why we need this? The first recover will try to render a beautiful - // error page for user, but the process can still panic again, then - // we have to just recover twice and send a simple error page that - // should not panic any more. - defer func() { - if err := recover(); err != nil { - combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, string(log.Stack(2))) - log.Error(combinedErr) - if setting.IsProd() { - http.Error(w, http.StatusText(500), 500) - } else { - http.Error(w, combinedErr, 500) - } - } - }() - - if err := recover(); err != nil { - combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, string(log.Stack(2))) - log.Error("%v", combinedErr) - - lc := middlewares.Locale(w, req) - - // TODO: this should be replaced by real session after macaron removed totally - sessionStore, err := sessionManager.Start(w, req) - if err != nil { - // Just invoke the above recover catch - panic("session(start): " + err.Error()) - } - - var store = dataStore{ - Data: templates.Vars{ - "Language": lc.Language(), - "CurrentURL": setting.AppSubURL + req.URL.RequestURI(), - "i18n": lc, - }, - } - - // Get user from session if logged in. - user, _ := sso.SignedInUser(req, w, &store, sessionStore) - if user != nil { - store.Data["IsSigned"] = true - store.Data["SignedUser"] = user - store.Data["SignedUserID"] = user.ID - store.Data["SignedUserName"] = user.Name - store.Data["IsAdmin"] = user.IsAdmin - } else { - store.Data["SignedUserID"] = int64(0) - store.Data["SignedUserName"] = "" - } - - w.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) - - if !setting.IsProd() { - store.Data["ErrorMsg"] = combinedErr - } - err = rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store.Data)) - if err != nil { - log.Error("%v", err) - } - } - }() - - next.ServeHTTP(w, req) - }) - } -} diff --git a/routers/routes/web.go b/routers/routes/web.go new file mode 100644 index 0000000000..2433618581 --- /dev/null +++ b/routers/routes/web.go @@ -0,0 +1,1044 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package routes + +import ( + "encoding/gob" + "fmt" + "net/http" + "os" + "path" + "strings" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" + "code.gitea.io/gitea/modules/httpcache" + "code.gitea.io/gitea/modules/lfs" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/metrics" + "code.gitea.io/gitea/modules/public" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/storage" + "code.gitea.io/gitea/modules/templates" + "code.gitea.io/gitea/modules/validation" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers" + "code.gitea.io/gitea/routers/admin" + apiv1 "code.gitea.io/gitea/routers/api/v1" + "code.gitea.io/gitea/routers/api/v1/misc" + "code.gitea.io/gitea/routers/dev" + "code.gitea.io/gitea/routers/events" + "code.gitea.io/gitea/routers/org" + "code.gitea.io/gitea/routers/private" + "code.gitea.io/gitea/routers/repo" + "code.gitea.io/gitea/routers/user" + userSetting "code.gitea.io/gitea/routers/user/setting" + "code.gitea.io/gitea/services/mailer" + + // to registers all internal adapters + _ "code.gitea.io/gitea/modules/session" + + "gitea.com/go-chi/captcha" + "gitea.com/go-chi/session" + "github.com/NYTimes/gziphandler" + "github.com/go-chi/chi/middleware" + "github.com/prometheus/client_golang/prometheus" + "github.com/tstranex/u2f" +) + +const ( + // GzipMinSize represents min size to compress for the body size of response + GzipMinSize = 1400 +) + +func commonMiddlewares() []func(http.Handler) http.Handler { + var handlers = []func(http.Handler) http.Handler{ + func(next http.Handler) http.Handler { + return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + next.ServeHTTP(context.NewResponse(resp), req) + }) + }, + middleware.RealIP, + } + if !setting.DisableRouterLog && setting.RouterLogLevel != log.NONE { + if log.GetLogger("router").GetLevel() <= setting.RouterLogLevel { + handlers = append(handlers, LoggerHandler(setting.RouterLogLevel)) + } + } + handlers = append(handlers, func(next http.Handler) http.Handler { + return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + // Why we need this? The Recovery() will try to render a beautiful + // error page for user, but the process can still panic again, and other + // middleware like session also may panic then we have to recover twice + // and send a simple error page that should not panic any more. + defer func() { + if err := recover(); err != nil { + combinedErr := fmt.Sprintf("PANIC: %v\n%s", err, string(log.Stack(2))) + log.Error("%v", combinedErr) + if setting.IsProd() { + http.Error(resp, http.StatusText(500), 500) + } else { + http.Error(resp, combinedErr, 500) + } + } + }() + next.ServeHTTP(resp, req) + }) + }) + + if setting.EnableAccessLog { + handlers = append(handlers, accessLogger()) + } + return handlers +} + +// NormalRoutes represents non install routes +func NormalRoutes() *web.Route { + r := web.NewRoute() + for _, middle := range commonMiddlewares() { + r.Use(middle) + } + r.Use(Recovery()) + + r.Mount("/", WebRoutes()) + r.Mount("/api/v1", apiv1.Routes()) + r.Mount("/api/internal", private.Routes()) + return r +} + +// WebRoutes returns all web routes +func WebRoutes() *web.Route { + r := web.NewRoute() + + r.Use(session.Sessioner(session.Options{ + Provider: setting.SessionConfig.Provider, + ProviderConfig: setting.SessionConfig.ProviderConfig, + CookieName: setting.SessionConfig.CookieName, + CookiePath: setting.SessionConfig.CookiePath, + Gclifetime: setting.SessionConfig.Gclifetime, + Maxlifetime: setting.SessionConfig.Maxlifetime, + Secure: setting.SessionConfig.Secure, + Domain: setting.SessionConfig.Domain, + })) + + r.Use(public.Custom( + &public.Options{ + SkipLogging: setting.DisableRouterLog, + }, + )) + r.Use(public.Static( + &public.Options{ + Directory: path.Join(setting.StaticRootPath, "public"), + SkipLogging: setting.DisableRouterLog, + }, + )) + + r.Use(storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars)) + r.Use(storageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars)) + + gob.Register(&u2f.Challenge{}) + + if setting.EnableGzip { + h, err := gziphandler.GzipHandlerWithOpts(gziphandler.MinSize(GzipMinSize)) + if err != nil { + log.Fatal("GzipHandlerWithOpts failed: %v", err) + } + r.Use(h) + } + + if (setting.Protocol == setting.FCGI || setting.Protocol == setting.FCGIUnix) && setting.AppSubURL != "" { + r.Use(func(next http.Handler) http.Handler { + return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + req.URL.Path = strings.TrimPrefix(req.URL.Path, setting.AppSubURL) + next.ServeHTTP(resp, req) + }) + }) + } + + mailer.InitMailRender(templates.Mailer()) + + r.Use(captcha.Captchaer(context.GetImageCaptcha())) + // Removed: toolbox.Toolboxer middleware will provide debug informations which seems unnecessary + r.Use(context.Contexter()) + // Removed: SetAutoHead allow a get request redirect to head if get method is not exist + + r.Use(user.GetNotificationCount) + r.Use(repo.GetActiveStopwatch) + r.Use(func(ctx *context.Context) { + ctx.Data["UnitWikiGlobalDisabled"] = models.UnitTypeWiki.UnitGlobalDisabled() + ctx.Data["UnitIssuesGlobalDisabled"] = models.UnitTypeIssues.UnitGlobalDisabled() + ctx.Data["UnitPullsGlobalDisabled"] = models.UnitTypePullRequests.UnitGlobalDisabled() + ctx.Data["UnitProjectsGlobalDisabled"] = models.UnitTypeProjects.UnitGlobalDisabled() + }) + + // for health check + r.Head("/", func(w http.ResponseWriter, req *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + if setting.HasRobotsTxt { + r.Get("/robots.txt", func(w http.ResponseWriter, req *http.Request) { + filePath := path.Join(setting.CustomPath, "robots.txt") + fi, err := os.Stat(filePath) + if err == nil && httpcache.HandleTimeCache(req, w, fi) { + return + } + http.ServeFile(w, req, filePath) + }) + } + + r.Get("/apple-touch-icon.png", func(w http.ResponseWriter, req *http.Request) { + http.Redirect(w, req, path.Join(setting.StaticURLPrefix, "img/apple-touch-icon.png"), 301) + }) + + // prometheus metrics endpoint + if setting.Metrics.Enabled { + c := metrics.NewCollector() + prometheus.MustRegister(c) + + r.Get("/metrics", routers.Metrics) + } + + if setting.API.EnableSwagger { + // Note: The route moved from apiroutes because it's in fact want to render a web page + r.Get("/api/swagger", misc.Swagger) // Render V1 by default + } + + RegisterRoutes(r) + + return r +} + +// RegisterRoutes routes routes to Macaron +func RegisterRoutes(m *web.Route) { + reqSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: true}) + ignSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: setting.Service.RequireSignInView}) + ignSignInAndCsrf := context.Toggle(&context.ToggleOptions{DisableCSRF: true}) + reqSignOut := context.Toggle(&context.ToggleOptions{SignOutRequired: true}) + + //bindIgnErr := binding.BindIgnErr + bindIgnErr := web.Bind + validation.AddBindingRules() + + openIDSignInEnabled := func(ctx *context.Context) { + if !setting.Service.EnableOpenIDSignIn { + ctx.Error(403) + return + } + } + + openIDSignUpEnabled := func(ctx *context.Context) { + if !setting.Service.EnableOpenIDSignUp { + ctx.Error(403) + return + } + } + + reqMilestonesDashboardPageEnabled := func(ctx *context.Context) { + if !setting.Service.ShowMilestonesDashboardPage { + ctx.Error(403) + return + } + } + + // FIXME: not all routes need go through same middlewares. + // Especially some AJAX requests, we can reduce middleware number to improve performance. + // Routers. + // for health check + m.Get("/", routers.Home) + m.Group("/explore", func() { + m.Get("", func(ctx *context.Context) { + ctx.Redirect(setting.AppSubURL + "/explore/repos") + }) + m.Get("/repos", routers.ExploreRepos) + m.Get("/users", routers.ExploreUsers) + m.Get("/organizations", routers.ExploreOrganizations) + m.Get("/code", routers.ExploreCode) + }, ignSignIn) + m.Get("/issues", reqSignIn, user.Issues) + m.Get("/pulls", reqSignIn, user.Pulls) + m.Get("/milestones", reqSignIn, reqMilestonesDashboardPageEnabled, user.Milestones) + + // ***** START: User ***** + m.Group("/user", func() { + m.Get("/login", user.SignIn) + m.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost) + m.Group("", func() { + m.Combo("/login/openid"). + Get(user.SignInOpenID). + Post(bindIgnErr(auth.SignInOpenIDForm{}), user.SignInOpenIDPost) + }, openIDSignInEnabled) + m.Group("/openid", func() { + m.Combo("/connect"). + Get(user.ConnectOpenID). + Post(bindIgnErr(auth.ConnectOpenIDForm{}), user.ConnectOpenIDPost) + m.Group("/register", func() { + m.Combo(""). + Get(user.RegisterOpenID, openIDSignUpEnabled). + Post(bindIgnErr(auth.SignUpOpenIDForm{}), user.RegisterOpenIDPost) + }, openIDSignUpEnabled) + }, openIDSignInEnabled) + m.Get("/sign_up", user.SignUp) + m.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost) + m.Group("/oauth2", func() { + m.Get("/{provider}", user.SignInOAuth) + m.Get("/{provider}/callback", user.SignInOAuthCallback) + }) + m.Get("/link_account", user.LinkAccount) + m.Post("/link_account_signin", bindIgnErr(auth.SignInForm{}), user.LinkAccountPostSignIn) + m.Post("/link_account_signup", bindIgnErr(auth.RegisterForm{}), user.LinkAccountPostRegister) + m.Group("/two_factor", func() { + m.Get("", user.TwoFactor) + m.Post("", bindIgnErr(auth.TwoFactorAuthForm{}), user.TwoFactorPost) + m.Get("/scratch", user.TwoFactorScratch) + m.Post("/scratch", bindIgnErr(auth.TwoFactorScratchAuthForm{}), user.TwoFactorScratchPost) + }) + m.Group("/u2f", func() { + m.Get("", user.U2F) + m.Get("/challenge", user.U2FChallenge) + m.Post("/sign", bindIgnErr(u2f.SignResponse{}), user.U2FSign) + + }) + }, reqSignOut) + + m.Any("/user/events", reqSignIn, events.Events) + + m.Group("/login/oauth", func() { + m.Get("/authorize", bindIgnErr(auth.AuthorizationForm{}), user.AuthorizeOAuth) + m.Post("/grant", bindIgnErr(auth.GrantApplicationForm{}), user.GrantApplicationOAuth) + // TODO manage redirection + m.Post("/authorize", bindIgnErr(auth.AuthorizationForm{}), user.AuthorizeOAuth) + }, ignSignInAndCsrf, reqSignIn) + m.Post("/login/oauth/access_token", bindIgnErr(auth.AccessTokenForm{}), ignSignInAndCsrf, user.AccessTokenOAuth) + + m.Group("/user/settings", func() { + m.Get("", userSetting.Profile) + m.Post("", bindIgnErr(auth.UpdateProfileForm{}), userSetting.ProfilePost) + m.Get("/change_password", user.MustChangePassword) + m.Post("/change_password", bindIgnErr(auth.MustChangePasswordForm{}), user.MustChangePasswordPost) + m.Post("/avatar", bindIgnErr(auth.AvatarForm{}), userSetting.AvatarPost) + m.Post("/avatar/delete", userSetting.DeleteAvatar) + m.Group("/account", func() { + m.Combo("").Get(userSetting.Account).Post(bindIgnErr(auth.ChangePasswordForm{}), userSetting.AccountPost) + m.Post("/email", bindIgnErr(auth.AddEmailForm{}), userSetting.EmailPost) + m.Post("/email/delete", userSetting.DeleteEmail) + m.Post("/delete", userSetting.DeleteAccount) + m.Post("/theme", bindIgnErr(auth.UpdateThemeForm{}), userSetting.UpdateUIThemePost) + }) + m.Group("/security", func() { + m.Get("", userSetting.Security) + m.Group("/two_factor", func() { + m.Post("/regenerate_scratch", userSetting.RegenerateScratchTwoFactor) + m.Post("/disable", userSetting.DisableTwoFactor) + m.Get("/enroll", userSetting.EnrollTwoFactor) + m.Post("/enroll", bindIgnErr(auth.TwoFactorAuthForm{}), userSetting.EnrollTwoFactorPost) + }) + m.Group("/u2f", func() { + m.Post("/request_register", bindIgnErr(auth.U2FRegistrationForm{}), userSetting.U2FRegister) + m.Post("/register", bindIgnErr(u2f.RegisterResponse{}), userSetting.U2FRegisterPost) + m.Post("/delete", bindIgnErr(auth.U2FDeleteForm{}), userSetting.U2FDelete) + }) + m.Group("/openid", func() { + m.Post("", bindIgnErr(auth.AddOpenIDForm{}), userSetting.OpenIDPost) + m.Post("/delete", userSetting.DeleteOpenID) + m.Post("/toggle_visibility", userSetting.ToggleOpenIDVisibility) + }, openIDSignInEnabled) + m.Post("/account_link", userSetting.DeleteAccountLink) + }) + m.Group("/applications/oauth2", func() { + m.Get("/{id}", userSetting.OAuth2ApplicationShow) + m.Post("/{id}", bindIgnErr(auth.EditOAuth2ApplicationForm{}), userSetting.OAuthApplicationsEdit) + m.Post("/{id}/regenerate_secret", userSetting.OAuthApplicationsRegenerateSecret) + m.Post("", bindIgnErr(auth.EditOAuth2ApplicationForm{}), userSetting.OAuthApplicationsPost) + m.Post("/delete", userSetting.DeleteOAuth2Application) + m.Post("/revoke", userSetting.RevokeOAuth2Grant) + }) + m.Combo("/applications").Get(userSetting.Applications). + Post(bindIgnErr(auth.NewAccessTokenForm{}), userSetting.ApplicationsPost) + m.Post("/applications/delete", userSetting.DeleteApplication) + m.Combo("/keys").Get(userSetting.Keys). + Post(bindIgnErr(auth.AddKeyForm{}), userSetting.KeysPost) + m.Post("/keys/delete", userSetting.DeleteKey) + m.Get("/organization", userSetting.Organization) + m.Get("/repos", userSetting.Repos) + m.Post("/repos/unadopted", userSetting.AdoptOrDeleteRepository) + }, reqSignIn, func(ctx *context.Context) { + ctx.Data["PageIsUserSettings"] = true + ctx.Data["AllThemes"] = setting.UI.Themes + }) + + m.Group("/user", func() { + // r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) + m.Any("/activate", user.Activate, reqSignIn) + m.Any("/activate_email", user.ActivateEmail) + m.Get("/avatar/{username}/{size}", user.Avatar) + m.Get("/email2user", user.Email2User) + m.Get("/recover_account", user.ResetPasswd) + m.Post("/recover_account", user.ResetPasswdPost) + m.Get("/forgot_password", user.ForgotPasswd) + m.Post("/forgot_password", user.ForgotPasswdPost) + m.Post("/logout", user.SignOut) + m.Get("/task/{task}", user.TaskStatus) + }) + // ***** END: User ***** + + m.Get("/avatar/{hash}", user.AvatarByEmailHash) + + adminReq := context.Toggle(&context.ToggleOptions{SignInRequired: true, AdminRequired: true}) + + // ***** START: Admin ***** + m.Group("/admin", func() { + m.Get("", adminReq, admin.Dashboard) + m.Post("", adminReq, bindIgnErr(auth.AdminDashboardForm{}), admin.DashboardPost) + m.Get("/config", admin.Config) + m.Post("/config/test_mail", admin.SendTestMail) + m.Group("/monitor", func() { + m.Get("", admin.Monitor) + m.Post("/cancel/{pid}", admin.MonitorCancel) + m.Group("/queue/{qid}", func() { + m.Get("", admin.Queue) + m.Post("/set", admin.SetQueueSettings) + m.Post("/add", admin.AddWorkers) + m.Post("/cancel/{pid}", admin.WorkerCancel) + m.Post("/flush", admin.Flush) + }) + }) + + m.Group("/users", func() { + m.Get("", admin.Users) + m.Combo("/new").Get(admin.NewUser).Post(bindIgnErr(auth.AdminCreateUserForm{}), admin.NewUserPost) + m.Combo("/{userid}").Get(admin.EditUser).Post(bindIgnErr(auth.AdminEditUserForm{}), admin.EditUserPost) + m.Post("/{userid}/delete", admin.DeleteUser) + }) + + m.Group("/emails", func() { + m.Get("", admin.Emails) + m.Post("/activate", admin.ActivateEmail) + }) + + m.Group("/orgs", func() { + m.Get("", admin.Organizations) + }) + + m.Group("/repos", func() { + m.Get("", admin.Repos) + m.Combo("/unadopted").Get(admin.UnadoptedRepos).Post(admin.AdoptOrDeleteRepository) + m.Post("/delete", admin.DeleteRepo) + }) + + m.Group("/hooks", func() { + m.Get("", admin.DefaultOrSystemWebhooks) + m.Post("/delete", admin.DeleteDefaultOrSystemWebhook) + m.Get("/{id}", repo.WebHooksEdit) + m.Post("/gitea/{id}", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) + m.Post("/gogs/{id}", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost) + m.Post("/slack/{id}", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) + m.Post("/discord/{id}", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) + m.Post("/dingtalk/{id}", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) + m.Post("/telegram/{id}", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost) + m.Post("/matrix/{id}", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksEditPost) + m.Post("/msteams/{id}", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) + m.Post("/feishu/{id}", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksEditPost) + }) + + m.Group("/{configType:default-hooks|system-hooks}", func() { + m.Get("/{type}/new", repo.WebhooksNew) + m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.GiteaHooksNewPost) + m.Post("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost) + m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) + m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost) + m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost) + m.Post("/telegram/new", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksNewPost) + m.Post("/matrix/new", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksNewPost) + m.Post("/msteams/new", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost) + m.Post("/feishu/new", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksNewPost) + }) + + m.Group("/auths", func() { + m.Get("", admin.Authentications) + m.Combo("/new").Get(admin.NewAuthSource).Post(bindIgnErr(auth.AuthenticationForm{}), admin.NewAuthSourcePost) + m.Combo("/{authid}").Get(admin.EditAuthSource). + Post(bindIgnErr(auth.AuthenticationForm{}), admin.EditAuthSourcePost) + m.Post("/{authid}/delete", admin.DeleteAuthSource) + }) + + m.Group("/notices", func() { + m.Get("", admin.Notices) + m.Post("/delete", admin.DeleteNotices) + m.Post("/empty", admin.EmptyNotices) + }) + }, adminReq) + // ***** END: Admin ***** + + m.Group("", func() { + m.Get("/{username}", user.Profile) + m.Get("/attachments/{uuid}", repo.GetAttachment) + }, ignSignIn) + + m.Group("/{username}", func() { + m.Post("/action/{action}", user.Action) + }, reqSignIn) + + if !setting.IsProd() { + m.Get("/template/*", dev.TemplatePreview) + } + + reqRepoAdmin := context.RequireRepoAdmin() + reqRepoCodeWriter := context.RequireRepoWriter(models.UnitTypeCode) + reqRepoCodeReader := context.RequireRepoReader(models.UnitTypeCode) + reqRepoReleaseWriter := context.RequireRepoWriter(models.UnitTypeReleases) + reqRepoReleaseReader := context.RequireRepoReader(models.UnitTypeReleases) + reqRepoWikiWriter := context.RequireRepoWriter(models.UnitTypeWiki) + reqRepoIssueWriter := context.RequireRepoWriter(models.UnitTypeIssues) + reqRepoIssueReader := context.RequireRepoReader(models.UnitTypeIssues) + reqRepoPullsReader := context.RequireRepoReader(models.UnitTypePullRequests) + reqRepoIssuesOrPullsWriter := context.RequireRepoWriterOr(models.UnitTypeIssues, models.UnitTypePullRequests) + reqRepoIssuesOrPullsReader := context.RequireRepoReaderOr(models.UnitTypeIssues, models.UnitTypePullRequests) + reqRepoProjectsReader := context.RequireRepoReader(models.UnitTypeProjects) + reqRepoProjectsWriter := context.RequireRepoWriter(models.UnitTypeProjects) + + // ***** START: Organization ***** + m.Group("/org", func() { + m.Group("", func() { + m.Get("/create", org.Create) + m.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.CreatePost) + }) + + m.Group("/{org}", func() { + m.Get("/dashboard", user.Dashboard) + m.Get("/dashboard/{team}", user.Dashboard) + m.Get("/issues", user.Issues) + m.Get("/issues/{team}", user.Issues) + m.Get("/pulls", user.Pulls) + m.Get("/pulls/{team}", user.Pulls) + m.Get("/milestones", reqMilestonesDashboardPageEnabled, user.Milestones) + m.Get("/milestones/{team}", reqMilestonesDashboardPageEnabled, user.Milestones) + m.Get("/members", org.Members) + m.Post("/members/action/{action}", org.MembersAction) + m.Get("/teams", org.Teams) + }, context.OrgAssignment(true, false, true)) + + m.Group("/{org}", func() { + m.Get("/teams/{team}", org.TeamMembers) + m.Get("/teams/{team}/repositories", org.TeamRepositories) + m.Post("/teams/{team}/action/{action}", org.TeamsAction) + m.Post("/teams/{team}/action/repo/{action}", org.TeamsRepoAction) + }, context.OrgAssignment(true, false, true)) + + m.Group("/{org}", func() { + m.Get("/teams/new", org.NewTeam) + m.Post("/teams/new", bindIgnErr(auth.CreateTeamForm{}), org.NewTeamPost) + m.Get("/teams/{team}/edit", org.EditTeam) + m.Post("/teams/{team}/edit", bindIgnErr(auth.CreateTeamForm{}), org.EditTeamPost) + m.Post("/teams/{team}/delete", org.DeleteTeam) + + m.Group("/settings", func() { + m.Combo("").Get(org.Settings). + Post(bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost) + m.Post("/avatar", bindIgnErr(auth.AvatarForm{}), org.SettingsAvatar) + m.Post("/avatar/delete", org.SettingsDeleteAvatar) + + m.Group("/hooks", func() { + m.Get("", org.Webhooks) + m.Post("/delete", org.DeleteWebhook) + m.Get("/{type}/new", repo.WebhooksNew) + m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.GiteaHooksNewPost) + m.Post("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost) + m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) + m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost) + m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost) + m.Post("/telegram/new", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksNewPost) + m.Post("/matrix/new", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksNewPost) + m.Post("/msteams/new", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost) + m.Post("/feishu/new", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksNewPost) + m.Get("/{id}", repo.WebHooksEdit) + m.Post("/gitea/{id}", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) + m.Post("/gogs/{id}", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost) + m.Post("/slack/{id}", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) + m.Post("/discord/{id}", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) + m.Post("/dingtalk/{id}", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) + m.Post("/telegram/{id}", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost) + m.Post("/matrix/{id}", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksEditPost) + m.Post("/msteams/{id}", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) + m.Post("/feishu/{id}", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksEditPost) + }) + + m.Group("/labels", func() { + m.Get("", org.RetrieveLabels, org.Labels) + m.Post("/new", bindIgnErr(auth.CreateLabelForm{}), org.NewLabel) + m.Post("/edit", bindIgnErr(auth.CreateLabelForm{}), org.UpdateLabel) + m.Post("/delete", org.DeleteLabel) + m.Post("/initialize", bindIgnErr(auth.InitializeLabelsForm{}), org.InitializeLabels) + }) + + m.Route("/delete", "GET,POST", org.SettingsDelete) + }) + }, context.OrgAssignment(true, true)) + }, reqSignIn) + // ***** END: Organization ***** + + // ***** START: Repository ***** + m.Group("/repo", func() { + m.Get("/create", repo.Create) + m.Post("/create", bindIgnErr(auth.CreateRepoForm{}), repo.CreatePost) + m.Get("/migrate", repo.Migrate) + m.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), repo.MigratePost) + m.Group("/fork", func() { + m.Combo("/{repoid}").Get(repo.Fork). + Post(bindIgnErr(auth.CreateRepoForm{}), repo.ForkPost) + }, context.RepoIDAssignment(), context.UnitTypes(), reqRepoCodeReader) + }, reqSignIn) + + // ***** Release Attachment Download without Signin + m.Get("/{username}/{reponame}/releases/download/{vTag}/{fileName}", ignSignIn, context.RepoAssignment(), repo.MustBeNotEmpty, repo.RedirectDownload) + + m.Group("/{username}/{reponame}", func() { + m.Group("/settings", func() { + m.Combo("").Get(repo.Settings). + Post(bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost) + m.Post("/avatar", bindIgnErr(auth.AvatarForm{}), repo.SettingsAvatar) + m.Post("/avatar/delete", repo.SettingsDeleteAvatar) + + m.Group("/collaboration", func() { + m.Combo("").Get(repo.Collaboration).Post(repo.CollaborationPost) + m.Post("/access_mode", repo.ChangeCollaborationAccessMode) + m.Post("/delete", repo.DeleteCollaboration) + m.Group("/team", func() { + m.Post("", repo.AddTeamPost) + m.Post("/delete", repo.DeleteTeam) + }) + }) + m.Group("/branches", func() { + m.Combo("").Get(repo.ProtectedBranch).Post(repo.ProtectedBranchPost) + m.Combo("/*").Get(repo.SettingsProtectedBranch). + Post(bindIgnErr(auth.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo.SettingsProtectedBranchPost) + }, repo.MustBeNotEmpty) + + m.Group("/hooks", func() { + m.Get("", repo.Webhooks) + m.Post("/delete", repo.DeleteWebhook) + m.Get("/{type}/new", repo.WebhooksNew) + m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.GiteaHooksNewPost) + m.Post("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost) + m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost) + m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost) + m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost) + m.Post("/telegram/new", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksNewPost) + m.Post("/matrix/new", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksNewPost) + m.Post("/msteams/new", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost) + m.Post("/feishu/new", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksNewPost) + m.Get("/{id}", repo.WebHooksEdit) + m.Post("/{id}/test", repo.TestWebhook) + m.Post("/gitea/{id}", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost) + m.Post("/gogs/{id}", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost) + m.Post("/slack/{id}", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost) + m.Post("/discord/{id}", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost) + m.Post("/dingtalk/{id}", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) + m.Post("/telegram/{id}", bindIgnErr(auth.NewTelegramHookForm{}), repo.TelegramHooksEditPost) + m.Post("/matrix/{id}", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksEditPost) + m.Post("/msteams/{id}", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) + m.Post("/feishu/{id}", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksEditPost) + + m.Group("/git", func() { + m.Get("", repo.GitHooks) + m.Combo("/{name}").Get(repo.GitHooksEdit). + Post(repo.GitHooksEditPost) + }, context.GitHookService()) + }) + + m.Group("/keys", func() { + m.Combo("").Get(repo.DeployKeys). + Post(bindIgnErr(auth.AddKeyForm{}), repo.DeployKeysPost) + m.Post("/delete", repo.DeleteDeployKey) + }) + + m.Group("/lfs", func() { + m.Get("/", repo.LFSFiles) + m.Get("/show/{oid}", repo.LFSFileGet) + m.Post("/delete/{oid}", repo.LFSDelete) + m.Get("/pointers", repo.LFSPointerFiles) + m.Post("/pointers/associate", repo.LFSAutoAssociate) + m.Get("/find", repo.LFSFileFind) + m.Group("/locks", func() { + m.Get("/", repo.LFSLocks) + m.Post("/", repo.LFSLockFile) + m.Post("/{lid}/unlock", repo.LFSUnlock) + }) + }) + + }, func(ctx *context.Context) { + ctx.Data["PageIsSettings"] = true + ctx.Data["LFSStartServer"] = setting.LFS.StartServer + }) + }, reqSignIn, context.RepoAssignment(), context.UnitTypes(), reqRepoAdmin, context.RepoRef()) + + m.Post("/{username}/{reponame}/action/{action}", reqSignIn, context.RepoAssignment(), context.UnitTypes(), repo.Action) + + // Grouping for those endpoints not requiring authentication + m.Group("/{username}/{reponame}", func() { + m.Group("/milestone", func() { + m.Get("/{id}", repo.MilestoneIssuesAndPulls) + }, reqRepoIssuesOrPullsReader, context.RepoRef()) + m.Combo("/compare/*", repo.MustBeNotEmpty, reqRepoCodeReader, repo.SetEditorconfigIfExists). + Get(ignSignIn, repo.SetDiffViewStyle, repo.CompareDiff). + Post(reqSignIn, context.RepoMustNotBeArchived(), reqRepoPullsReader, repo.MustAllowPulls, bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost) + }, context.RepoAssignment(), context.UnitTypes()) + + // Grouping for those endpoints that do require authentication + m.Group("/{username}/{reponame}", func() { + m.Group("/issues", func() { + m.Group("/new", func() { + m.Combo("").Get(context.RepoRef(), repo.NewIssue). + Post(bindIgnErr(auth.CreateIssueForm{}), repo.NewIssuePost) + m.Get("/choose", context.RepoRef(), repo.NewIssueChooseTemplate) + }) + }, context.RepoMustNotBeArchived(), reqRepoIssueReader) + // FIXME: should use different URLs but mostly same logic for comments of issue and pull reuqest. + // So they can apply their own enable/disable logic on routers. + m.Group("/issues", func() { + m.Group("/{index}", func() { + m.Post("/title", repo.UpdateIssueTitle) + m.Post("/content", repo.UpdateIssueContent) + m.Post("/watch", repo.IssueWatch) + m.Post("/ref", repo.UpdateIssueRef) + m.Group("/dependency", func() { + m.Post("/add", repo.AddDependency) + m.Post("/delete", repo.RemoveDependency) + }) + m.Combo("/comments").Post(repo.MustAllowUserComment, bindIgnErr(auth.CreateCommentForm{}), repo.NewComment) + m.Group("/times", func() { + m.Post("/add", bindIgnErr(auth.AddTimeManuallyForm{}), repo.AddTimeManually) + m.Group("/stopwatch", func() { + m.Post("/toggle", repo.IssueStopwatch) + m.Post("/cancel", repo.CancelStopwatch) + }) + }) + m.Post("/reactions/{action}", bindIgnErr(auth.ReactionForm{}), repo.ChangeIssueReaction) + m.Post("/lock", reqRepoIssueWriter, bindIgnErr(auth.IssueLockForm{}), repo.LockIssue) + m.Post("/unlock", reqRepoIssueWriter, repo.UnlockIssue) + }, context.RepoMustNotBeArchived()) + m.Group("/{index}", func() { + m.Get("/attachments", repo.GetIssueAttachments) + m.Get("/attachments/{uuid}", repo.GetAttachment) + }) + + m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel) + m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone) + m.Post("/projects", reqRepoIssuesOrPullsWriter, repo.UpdateIssueProject) + m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee) + m.Post("/request_review", reqRepoIssuesOrPullsReader, repo.UpdatePullReviewRequest) + m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus) + m.Post("/resolve_conversation", reqRepoIssuesOrPullsReader, repo.UpdateResolveConversation) + m.Post("/attachments", repo.UploadIssueAttachment) + m.Post("/attachments/remove", repo.DeleteAttachment) + }, context.RepoMustNotBeArchived()) + m.Group("/comments/{id}", func() { + m.Post("", repo.UpdateCommentContent) + m.Post("/delete", repo.DeleteComment) + m.Post("/reactions/{action}", bindIgnErr(auth.ReactionForm{}), repo.ChangeCommentReaction) + }, context.RepoMustNotBeArchived()) + m.Group("/comments/{id}", func() { + m.Get("/attachments", repo.GetCommentAttachments) + }) + m.Group("/labels", func() { + m.Post("/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel) + m.Post("/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel) + m.Post("/delete", repo.DeleteLabel) + m.Post("/initialize", bindIgnErr(auth.InitializeLabelsForm{}), repo.InitializeLabels) + }, context.RepoMustNotBeArchived(), reqRepoIssuesOrPullsWriter, context.RepoRef()) + m.Group("/milestones", func() { + m.Combo("/new").Get(repo.NewMilestone). + Post(bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost) + m.Get("/{id}/edit", repo.EditMilestone) + m.Post("/{id}/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.EditMilestonePost) + m.Post("/{id}/{action}", repo.ChangeMilestoneStatus) + m.Post("/delete", repo.DeleteMilestone) + }, context.RepoMustNotBeArchived(), reqRepoIssuesOrPullsWriter, context.RepoRef()) + m.Group("/pull", func() { + m.Post("/{index}/target_branch", repo.UpdatePullRequestTarget) + }, context.RepoMustNotBeArchived()) + + m.Group("", func() { + m.Group("", func() { + m.Combo("/_edit/*").Get(repo.EditFile). + Post(bindIgnErr(auth.EditRepoFileForm{}), repo.EditFilePost) + m.Combo("/_new/*").Get(repo.NewFile). + Post(bindIgnErr(auth.EditRepoFileForm{}), repo.NewFilePost) + m.Post("/_preview/*", bindIgnErr(auth.EditPreviewDiffForm{}), repo.DiffPreviewPost) + m.Combo("/_delete/*").Get(repo.DeleteFile). + Post(bindIgnErr(auth.DeleteRepoFileForm{}), repo.DeleteFilePost) + m.Combo("/_upload/*", repo.MustBeAbleToUpload). + Get(repo.UploadFile). + Post(bindIgnErr(auth.UploadRepoFileForm{}), repo.UploadFilePost) + }, context.RepoRefByType(context.RepoRefBranch), repo.MustBeEditable) + m.Group("", func() { + m.Post("/upload-file", repo.UploadFileToServer) + m.Post("/upload-remove", bindIgnErr(auth.RemoveUploadFileForm{}), repo.RemoveUploadFileFromServer) + }, context.RepoRef(), repo.MustBeEditable, repo.MustBeAbleToUpload) + }, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty) + + m.Group("/branches", func() { + m.Group("/_new", func() { + m.Post("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.CreateBranch) + m.Post("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.CreateBranch) + m.Post("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.CreateBranch) + }, bindIgnErr(auth.NewBranchForm{})) + m.Post("/delete", repo.DeleteBranchPost) + m.Post("/restore", repo.RestoreBranchPost) + }, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty) + + }, reqSignIn, context.RepoAssignment(), context.UnitTypes()) + + // Releases + m.Group("/{username}/{reponame}", func() { + m.Get("/tags", repo.TagsList, repo.MustBeNotEmpty, + reqRepoCodeReader, context.RepoRefByType(context.RepoRefTag)) + m.Group("/releases", func() { + m.Get("/", repo.Releases) + m.Get("/tag/*", repo.SingleRelease) + m.Get("/latest", repo.LatestRelease) + m.Get("/attachments/{uuid}", repo.GetAttachment) + }, repo.MustBeNotEmpty, reqRepoReleaseReader, context.RepoRefByType(context.RepoRefTag)) + m.Group("/releases", func() { + m.Get("/new", repo.NewRelease) + m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost) + m.Post("/delete", repo.DeleteRelease) + m.Post("/attachments", repo.UploadReleaseAttachment) + m.Post("/attachments/remove", repo.DeleteAttachment) + }, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, context.RepoRef()) + m.Post("/tags/delete", repo.DeleteTag, reqSignIn, + repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoCodeWriter, context.RepoRef()) + m.Group("/releases", func() { + m.Get("/edit/*", repo.EditRelease) + m.Post("/edit/*", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost) + }, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, func(ctx *context.Context) { + var err error + ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch) + if err != nil { + ctx.ServerError("GetBranchCommit", err) + return + } + ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount() + if err != nil { + ctx.ServerError("GetCommitsCount", err) + return + } + ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount + }) + }, ignSignIn, context.RepoAssignment(), context.UnitTypes(), reqRepoReleaseReader) + + m.Group("/{username}/{reponame}", func() { + m.Post("/topics", repo.TopicsPost) + }, context.RepoAssignment(), context.RepoMustNotBeArchived(), reqRepoAdmin) + + m.Group("/{username}/{reponame}", func() { + m.Group("", func() { + m.Get("/{type:issues|pulls}", repo.Issues) + m.Get("/{type:issues|pulls}/{index}", repo.ViewIssue) + m.Get("/labels", reqRepoIssuesOrPullsReader, repo.RetrieveLabels, repo.Labels) + m.Get("/milestones", reqRepoIssuesOrPullsReader, repo.Milestones) + }, context.RepoRef()) + + m.Group("/projects", func() { + m.Get("", repo.Projects) + m.Get("/{id}", repo.ViewProject) + m.Group("", func() { + m.Get("/new", repo.NewProject) + m.Post("/new", bindIgnErr(auth.CreateProjectForm{}), repo.NewProjectPost) + m.Group("/{id}", func() { + m.Post("", bindIgnErr(auth.EditProjectBoardTitleForm{}), repo.AddBoardToProjectPost) + m.Post("/delete", repo.DeleteProject) + + m.Get("/edit", repo.EditProject) + m.Post("/edit", bindIgnErr(auth.CreateProjectForm{}), repo.EditProjectPost) + m.Post("/{action:open|close}", repo.ChangeProjectStatus) + + m.Group("/{boardID}", func() { + m.Put("", bindIgnErr(auth.EditProjectBoardTitleForm{}), repo.EditProjectBoardTitle) + m.Delete("", repo.DeleteProjectBoard) + m.Post("/default", repo.SetDefaultProjectBoard) + + m.Post("/{index}", repo.MoveIssueAcrossBoards) + }) + }) + }, reqRepoProjectsWriter, context.RepoMustNotBeArchived()) + }, reqRepoProjectsReader, repo.MustEnableProjects) + + m.Group("/wiki", func() { + m.Get("/", repo.Wiki) + m.Get("/{page}", repo.Wiki) + m.Get("/_pages", repo.WikiPages) + m.Get("/{page}/_revision", repo.WikiRevision) + m.Get("/commit/{sha:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff) + m.Get("/commit/{sha:[a-f0-9]{7,40}}.{:patch|diff}", repo.RawDiff) + + m.Group("", func() { + m.Combo("/_new").Get(repo.NewWiki). + Post(bindIgnErr(auth.NewWikiForm{}), repo.NewWikiPost) + m.Combo("/{page}/_edit").Get(repo.EditWiki). + Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost) + m.Post("/{page}/delete", repo.DeleteWikiPagePost) + }, context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter) + }, repo.MustEnableWiki, context.RepoRef(), func(ctx *context.Context) { + ctx.Data["PageIsWiki"] = true + }) + + m.Group("/wiki", func() { + m.Get("/raw/*", repo.WikiRaw) + }, repo.MustEnableWiki) + + m.Group("/activity", func() { + m.Get("", repo.Activity) + m.Get("/{period}", repo.Activity) + }, context.RepoRef(), repo.MustBeNotEmpty, context.RequireRepoReaderOr(models.UnitTypePullRequests, models.UnitTypeIssues, models.UnitTypeReleases)) + + m.Group("/activity_author_data", func() { + m.Get("", repo.ActivityAuthors) + m.Get("/{period}", repo.ActivityAuthors) + }, context.RepoRef(), repo.MustBeNotEmpty, context.RequireRepoReaderOr(models.UnitTypeCode)) + + m.Group("/archive", func() { + m.Get("/*", repo.Download) + m.Post("/*", repo.InitiateDownload) + }, repo.MustBeNotEmpty, reqRepoCodeReader) + + m.Group("/branches", func() { + m.Get("", repo.Branches) + }, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) + + m.Group("/blob_excerpt", func() { + m.Get("/{sha}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ExcerptBlob) + }, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) + + m.Group("/pulls/{index}", func() { + m.Get(".diff", repo.DownloadPullDiff) + m.Get(".patch", repo.DownloadPullPatch) + m.Get("/commits", context.RepoRef(), repo.ViewPullCommits) + m.Post("/merge", context.RepoMustNotBeArchived(), bindIgnErr(auth.MergePullRequestForm{}), repo.MergePullRequest) + m.Post("/update", repo.UpdatePullRequest) + m.Post("/cleanup", context.RepoMustNotBeArchived(), context.RepoRef(), repo.CleanUpPullRequest) + m.Group("/files", func() { + m.Get("", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.ViewPullFiles) + m.Group("/reviews", func() { + m.Get("/new_comment", repo.RenderNewCodeCommentForm) + m.Post("/comments", bindIgnErr(auth.CodeCommentForm{}), repo.CreateCodeComment) + m.Post("/submit", bindIgnErr(auth.SubmitReviewForm{}), repo.SubmitReview) + }, context.RepoMustNotBeArchived()) + }) + }, repo.MustAllowPulls) + + m.Group("/media", func() { + m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.SingleDownloadOrLFS) + m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.SingleDownloadOrLFS) + m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.SingleDownloadOrLFS) + m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByIDOrLFS) + // "/*" route is deprecated, and kept for backward compatibility + m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownloadOrLFS) + }, repo.MustBeNotEmpty, reqRepoCodeReader) + + m.Group("/raw", func() { + m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.SingleDownload) + m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.SingleDownload) + m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.SingleDownload) + m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByID) + // "/*" route is deprecated, and kept for backward compatibility + m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownload) + }, repo.MustBeNotEmpty, reqRepoCodeReader) + + m.Group("/commits", func() { + m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefCommits) + m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RefCommits) + m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RefCommits) + // "/*" route is deprecated, and kept for backward compatibility + m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.RefCommits) + }, repo.MustBeNotEmpty, reqRepoCodeReader) + + m.Group("/blame", func() { + m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefBlame) + m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RefBlame) + m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RefBlame) + }, repo.MustBeNotEmpty, reqRepoCodeReader) + + m.Group("", func() { + m.Get("/graph", repo.Graph) + m.Get("/commit/{sha:([a-f0-9]{7,40})$}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff) + }, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) + + m.Group("/src", func() { + m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home) + m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.Home) + m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.Home) + // "/*" route is deprecated, and kept for backward compatibility + m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.Home) + }, repo.SetEditorconfigIfExists) + + m.Group("", func() { + m.Get("/forks", repo.Forks) + }, context.RepoRef(), reqRepoCodeReader) + m.Get("/commit/{sha:([a-f0-9]{7,40})}.{ext:patch|diff}", + repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff) + }, ignSignIn, context.RepoAssignment(), context.UnitTypes()) + m.Group("/{username}/{reponame}", func() { + m.Get("/stars", repo.Stars) + m.Get("/watchers", repo.Watchers) + m.Get("/search", reqRepoCodeReader, repo.Search) + }, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes()) + + m.Group("/{username}", func() { + m.Group("/{reponame}", func() { + m.Get("", repo.SetEditorconfigIfExists, repo.Home) + }, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes()) + + m.Group("/{reponame}", func() { + m.Group("/info/lfs", func() { + m.Post("/objects/batch", lfs.BatchHandler) + m.Get("/objects/{oid}/{filename}", lfs.ObjectOidHandler) + m.Any("/objects/{oid}", lfs.ObjectOidHandler) + m.Post("/objects", lfs.PostHandler) + m.Post("/verify", lfs.VerifyHandler) + m.Group("/locks", func() { + m.Get("/", lfs.GetListLockHandler) + m.Post("/", lfs.PostLockHandler) + m.Post("/verify", lfs.VerifyLockHandler) + m.Post("/{lid}/unlock", lfs.UnLockHandler) + }) + m.Any("/*", func(ctx *context.Context) { + ctx.NotFound("", nil) + }) + }, ignSignInAndCsrf) + + m.Group("", func() { + m.Post("/git-upload-pack", repo.ServiceUploadPack) + m.Post("/git-receive-pack", repo.ServiceReceivePack) + m.Get("/info/refs", repo.GetInfoRefs) + m.Get("/HEAD", repo.GetTextFile("HEAD")) + m.Get("/objects/info/alternates", repo.GetTextFile("objects/info/alternates")) + m.Get("/objects/info/http-alternates", repo.GetTextFile("objects/info/http-alternates")) + m.Get("/objects/info/packs", repo.GetInfoPacks) + m.Get("/objects/info/{file:[^/]*}", repo.GetTextFile("")) + m.Get("/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject) + m.Get("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile) + m.Get("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile) + }, ignSignInAndCsrf) + + m.Head("/tasks/trigger", repo.TriggerTask) + }) + }) + // ***** END: Repository ***** + + m.Group("/notifications", func() { + m.Get("", user.Notifications) + m.Post("/status", user.NotificationStatusPost) + m.Post("/purge", user.NotificationPurgePost) + }, reqSignIn) + + if setting.API.EnableSwagger { + m.Get("/swagger.v1.json", routers.SwaggerV1Json) + } + + // Not found handler. + m.NotFound(web.Wrap(routers.NotFound)) +} diff --git a/routers/swagger_json.go b/routers/swagger_json.go index 58c4ec50d9..3ff1674f04 100644 --- a/routers/swagger_json.go +++ b/routers/swagger_json.go @@ -7,6 +7,7 @@ package routers import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" ) // tplSwaggerV1Json swagger v1 json template @@ -14,5 +15,10 @@ const tplSwaggerV1Json base.TplName = "swagger/v1_json" // SwaggerV1Json render swagger v1 json func SwaggerV1Json(ctx *context.Context) { - ctx.HTML(200, tplSwaggerV1Json) + t := ctx.Render.TemplateLookup(string(tplSwaggerV1Json)) + ctx.Resp.Header().Set("Content-Type", "application/json") + if err := t.Execute(ctx.Resp, ctx.Data); err != nil { + log.Error("%v", err) + ctx.Error(500) + } } diff --git a/routers/user/auth.go b/routers/user/auth.go index bce801847d..909d0a2ee5 100644 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -12,22 +12,22 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/auth/oauth2" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/eventsource" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/hcaptcha" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/password" "code.gitea.io/gitea/modules/recaptcha" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" "code.gitea.io/gitea/services/externalaccount" "code.gitea.io/gitea/services/mailer" - "gitea.com/macaron/captcha" "github.com/markbates/goth" "github.com/tstranex/u2f" ) @@ -149,7 +149,7 @@ func SignIn(ctx *context.Context) { } // SignInPost response for sign in request -func SignInPost(ctx *context.Context, form auth.SignInForm) { +func SignInPost(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("sign_in") orderedOAuth2Names, oauth2Providers, err := models.GetActiveOAuth2Providers() @@ -170,6 +170,7 @@ func SignInPost(ctx *context.Context, form auth.SignInForm) { return } + form := web.GetForm(ctx).(*auth.SignInForm) u, err := models.UserSignIn(form.UserName, form.Password) if err != nil { if models.IsErrUserNotExist(err) { @@ -250,7 +251,8 @@ func TwoFactor(ctx *context.Context) { } // TwoFactorPost validates a user's two-factor authentication token. -func TwoFactorPost(ctx *context.Context, form auth.TwoFactorAuthForm) { +func TwoFactorPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.TwoFactorAuthForm) ctx.Data["Title"] = ctx.Tr("twofa") // Ensure user is in a 2FA session. @@ -328,7 +330,8 @@ func TwoFactorScratch(ctx *context.Context) { } // TwoFactorScratchPost validates and invalidates a user's two-factor scratch token. -func TwoFactorScratchPost(ctx *context.Context, form auth.TwoFactorScratchAuthForm) { +func TwoFactorScratchPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.TwoFactorScratchAuthForm) ctx.Data["Title"] = ctx.Tr("twofa_scratch") // Ensure user is in a 2FA session. @@ -427,7 +430,8 @@ func U2FChallenge(ctx *context.Context) { } // U2FSign authenticates the user by signResp -func U2FSign(ctx *context.Context, signResp u2f.SignResponse) { +func U2FSign(ctx *context.Context) { + signResp := web.GetForm(ctx).(*u2f.SignResponse) challSess := ctx.Session.Get("u2fChallenge") idSess := ctx.Session.Get("twofaUid") if challSess == nil || idSess == nil { @@ -447,7 +451,7 @@ func U2FSign(ctx *context.Context, signResp u2f.SignResponse) { log.Fatal("parsing u2f registration: %v", err) continue } - newCounter, authErr := r.Authenticate(signResp, *challenge, reg.Counter) + newCounter, authErr := r.Authenticate(*signResp, *challenge, reg.Counter) if authErr == nil { reg.Counter = newCounter user, err := models.GetUserByID(id) @@ -563,20 +567,20 @@ func SignInOAuth(ctx *context.Context) { } // try to do a direct callback flow, so we don't authenticate the user again but use the valid accesstoken to get the user - user, gothUser, err := oAuth2UserLoginCallback(loginSource, ctx.Req.Request, ctx.Resp) + user, gothUser, err := oAuth2UserLoginCallback(loginSource, ctx.Req, ctx.Resp) if err == nil && user != nil { // we got the user without going through the whole OAuth2 authentication flow again handleOAuth2SignIn(user, gothUser, ctx, err) return } - if err = oauth2.Auth(loginSource.Name, ctx.Req.Request, ctx.Resp); err != nil { + if err = oauth2.Auth(loginSource.Name, ctx.Req, ctx.Resp); err != nil { if strings.Contains(err.Error(), "no provider for ") { if err = models.ResetOAuth2(); err != nil { ctx.ServerError("SignIn", err) return } - if err = oauth2.Auth(loginSource.Name, ctx.Req.Request, ctx.Resp); err != nil { + if err = oauth2.Auth(loginSource.Name, ctx.Req, ctx.Resp); err != nil { ctx.ServerError("SignIn", err) } return @@ -602,7 +606,7 @@ func SignInOAuthCallback(ctx *context.Context) { return } - u, gothUser, err := oAuth2UserLoginCallback(loginSource, ctx.Req.Request, ctx.Resp) + u, gothUser, err := oAuth2UserLoginCallback(loginSource, ctx.Req, ctx.Resp) handleOAuth2SignIn(u, gothUser, ctx, err) } @@ -788,7 +792,8 @@ func LinkAccount(ctx *context.Context) { } // LinkAccountPostSignIn handle the coupling of external account with another account using signIn -func LinkAccountPostSignIn(ctx *context.Context, signInForm auth.SignInForm) { +func LinkAccountPostSignIn(ctx *context.Context) { + signInForm := web.GetForm(ctx).(*auth.SignInForm) ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationPassword || setting.Service.AllowOnlyExternalRegistration ctx.Data["Title"] = ctx.Tr("link_account") ctx.Data["LinkAccountMode"] = true @@ -870,7 +875,8 @@ func LinkAccountPostSignIn(ctx *context.Context, signInForm auth.SignInForm) { } // LinkAccountPostRegister handle the creation of a new account for an external account using signUp -func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterForm) { +func LinkAccountPostRegister(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.RegisterForm) // TODO Make insecure passwords optional for local accounts also, // once email-based Second-Factor Auth is available ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationPassword || setting.Service.AllowOnlyExternalRegistration @@ -909,7 +915,7 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au var err error switch setting.Service.CaptchaType { case setting.ImageCaptcha: - valid = cpt.VerifyReq(ctx.Req) + valid = context.GetImageCaptcha().VerifyReq(ctx.Req) case setting.ReCaptcha: valid, err = recaptcha.Verify(ctx.Req.Context(), form.GRecaptchaResponse) case setting.HCaptcha: @@ -1029,7 +1035,7 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au // HandleSignOut resets the session and sets the cookies func HandleSignOut(ctx *context.Context) { _ = ctx.Session.Flush() - _ = ctx.Session.Destroy(ctx.Context) + _ = ctx.Session.Destroy(ctx.Resp, ctx.Req) ctx.SetCookie(setting.CookieUserName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) ctx.SetCookie(setting.CookieRememberName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) @@ -1069,7 +1075,8 @@ func SignUp(ctx *context.Context) { } // SignUpPost response for sign up information submission -func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterForm) { +func SignUpPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.RegisterForm) ctx.Data["Title"] = ctx.Tr("sign_up") ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/sign_up" @@ -1097,7 +1104,7 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo var err error switch setting.Service.CaptchaType { case setting.ImageCaptcha: - valid = cpt.VerifyReq(ctx.Req) + valid = context.GetImageCaptcha().VerifyReq(ctx.Req) case setting.ReCaptcha: valid, err = recaptcha.Verify(ctx.Req.Context(), form.GRecaptchaResponse) case setting.HCaptcha: @@ -1562,7 +1569,8 @@ func MustChangePassword(ctx *context.Context) { // MustChangePasswordPost response for updating a user's password after his/her // account was created by an admin -func MustChangePasswordPost(ctx *context.Context, cpt *captcha.Captcha, form auth.MustChangePasswordForm) { +func MustChangePasswordPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.MustChangePasswordForm) ctx.Data["Title"] = ctx.Tr("auth.must_change_password") ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/settings/change_password" if ctx.HasError() { diff --git a/routers/user/auth_openid.go b/routers/user/auth_openid.go index 39e75f202d..1efcc7eda8 100644 --- a/routers/user/auth_openid.go +++ b/routers/user/auth_openid.go @@ -9,19 +9,18 @@ import ( "net/url" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/auth/openid" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/generate" "code.gitea.io/gitea/modules/hcaptcha" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/recaptcha" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/mailer" - - "gitea.com/macaron/captcha" ) const ( @@ -90,7 +89,8 @@ func allowedOpenIDURI(uri string) (err error) { } // SignInOpenIDPost response for openid sign in request -func SignInOpenIDPost(ctx *context.Context, form auth.SignInOpenIDForm) { +func SignInOpenIDPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.SignInOpenIDForm) ctx.Data["Title"] = ctx.Tr("sign_in") ctx.Data["PageIsSignIn"] = true ctx.Data["PageIsLoginOpenID"] = true @@ -143,9 +143,9 @@ func SignInOpenIDPost(ctx *context.Context, form auth.SignInOpenIDForm) { // signInOpenIDVerify handles response from OpenID provider func signInOpenIDVerify(ctx *context.Context) { - log.Trace("Incoming call to: " + ctx.Req.Request.URL.String()) + log.Trace("Incoming call to: " + ctx.Req.URL.String()) - fullURL := setting.AppURL + ctx.Req.Request.URL.String()[1:] + fullURL := setting.AppURL + ctx.Req.URL.String()[1:] log.Trace("Full URL: " + fullURL) var id, err = openid.Verify(fullURL) @@ -276,8 +276,8 @@ func ConnectOpenID(ctx *context.Context) { } // ConnectOpenIDPost handles submission of a form to connect an OpenID URI to an existing account -func ConnectOpenIDPost(ctx *context.Context, form auth.ConnectOpenIDForm) { - +func ConnectOpenIDPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.ConnectOpenIDForm) oid, _ := ctx.Session.Get("openid_verified_uri").(string) if oid == "" { ctx.Redirect(setting.AppSubURL + "/user/login/openid") @@ -346,7 +346,8 @@ func RegisterOpenID(ctx *context.Context) { } // RegisterOpenIDPost handles submission of a form to create a new user authenticated via an OpenID URI -func RegisterOpenIDPost(ctx *context.Context, cpt *captcha.Captcha, form auth.SignUpOpenIDForm) { +func RegisterOpenIDPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.SignUpOpenIDForm) oid, _ := ctx.Session.Get("openid_verified_uri").(string) if oid == "" { ctx.Redirect(setting.AppSubURL + "/user/login/openid") @@ -369,7 +370,7 @@ func RegisterOpenIDPost(ctx *context.Context, cpt *captcha.Captcha, form auth.Si var err error switch setting.Service.CaptchaType { case setting.ImageCaptcha: - valid = cpt.VerifyReq(ctx.Req) + valid = context.GetImageCaptcha().VerifyReq(ctx.Req) case setting.ReCaptcha: if err := ctx.Req.ParseForm(); err != nil { ctx.ServerError("", err) diff --git a/routers/user/oauth.go b/routers/user/oauth.go index dda1268f8a..d943ec4200 100644 --- a/routers/user/oauth.go +++ b/routers/user/oauth.go @@ -12,14 +12,15 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/web" - "gitea.com/macaron/binding" + "gitea.com/go-chi/binding" "github.com/dgrijalva/jwt-go" ) @@ -192,9 +193,10 @@ func newAccessTokenResponse(grant *models.OAuth2Grant, clientSecret string) (*Ac } // AuthorizeOAuth manages authorize requests -func AuthorizeOAuth(ctx *context.Context, form auth.AuthorizationForm) { +func AuthorizeOAuth(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AuthorizationForm) errs := binding.Errors{} - errs = form.Validate(ctx.Context, errs) + errs = form.Validate(ctx.Req, errs) if len(errs) > 0 { errstring := "" for _, e := range errs { @@ -341,7 +343,8 @@ func AuthorizeOAuth(ctx *context.Context, form auth.AuthorizationForm) { } // GrantApplicationOAuth manages the post request submitted when a user grants access to an application -func GrantApplicationOAuth(ctx *context.Context, form auth.GrantApplicationForm) { +func GrantApplicationOAuth(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.GrantApplicationForm) if ctx.Session.Get("client_id") != form.ClientID || ctx.Session.Get("state") != form.State || ctx.Session.Get("redirect_uri") != form.RedirectURI { ctx.Error(400) @@ -386,7 +389,8 @@ func GrantApplicationOAuth(ctx *context.Context, form auth.GrantApplicationForm) } // AccessTokenOAuth manages all access token requests by the client -func AccessTokenOAuth(ctx *context.Context, form auth.AccessTokenForm) { +func AccessTokenOAuth(ctx *context.Context) { + form := *web.GetForm(ctx).(*auth.AccessTokenForm) if form.ClientID == "" { authHeader := ctx.Req.Header.Get("Authorization") authContent := strings.SplitN(authHeader, " ", 2) diff --git a/routers/user/setting/account.go b/routers/user/setting/account.go index 42c2c59b7e..4900bba14a 100644 --- a/routers/user/setting/account.go +++ b/routers/user/setting/account.go @@ -10,13 +10,14 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/password" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/mailer" ) @@ -36,7 +37,8 @@ func Account(ctx *context.Context) { } // AccountPost response for change user's password -func AccountPost(ctx *context.Context, form auth.ChangePasswordForm) { +func AccountPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.ChangePasswordForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsAccount"] = true @@ -80,7 +82,8 @@ func AccountPost(ctx *context.Context, form auth.ChangePasswordForm) { } // EmailPost response for change user's email -func EmailPost(ctx *context.Context, form auth.AddEmailForm) { +func EmailPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AddEmailForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsAccount"] = true @@ -252,8 +255,8 @@ func DeleteAccount(ctx *context.Context) { } // UpdateUIThemePost is used to update users' specific theme -func UpdateUIThemePost(ctx *context.Context, form auth.UpdateThemeForm) { - +func UpdateUIThemePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.UpdateThemeForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsAccount"] = true diff --git a/routers/user/setting/account_test.go b/routers/user/setting/account_test.go index 841ecb8ac2..0e7e147b8b 100644 --- a/routers/user/setting/account_test.go +++ b/routers/user/setting/account_test.go @@ -9,9 +9,10 @@ import ( "testing" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/modules/web" "github.com/stretchr/testify/assert" ) @@ -85,11 +86,12 @@ func TestChangePassword(t *testing.T) { test.LoadUser(t, ctx, 2) test.LoadRepo(t, ctx, 1) - AccountPost(ctx, auth.ChangePasswordForm{ + web.SetForm(ctx, &auth.ChangePasswordForm{ OldPassword: req.OldPassword, Password: req.NewPassword, Retype: req.Retype, }) + AccountPost(ctx) assert.Contains(t, ctx.Flash.ErrorMsg, req.Message) assert.EqualValues(t, http.StatusFound, ctx.Resp.Status()) diff --git a/routers/user/setting/applications.go b/routers/user/setting/applications.go index 04f9d9f7f9..8da36dc6cf 100644 --- a/routers/user/setting/applications.go +++ b/routers/user/setting/applications.go @@ -7,10 +7,11 @@ package setting import ( "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" ) const ( @@ -28,7 +29,8 @@ func Applications(ctx *context.Context) { } // ApplicationsPost response for add user's access token -func ApplicationsPost(ctx *context.Context, form auth.NewAccessTokenForm) { +func ApplicationsPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.NewAccessTokenForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsApplications"] = true diff --git a/routers/user/setting/keys.go b/routers/user/setting/keys.go index 76c7ef9da4..a52ffd667b 100644 --- a/routers/user/setting/keys.go +++ b/routers/user/setting/keys.go @@ -7,10 +7,11 @@ package setting import ( "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" ) const ( @@ -31,7 +32,8 @@ func Keys(ctx *context.Context) { } // KeysPost response for change user's SSH/GPG keys -func KeysPost(ctx *context.Context, form auth.AddKeyForm) { +func KeysPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AddKeyForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsKeys"] = true ctx.Data["DisableSSH"] = setting.SSH.Disabled diff --git a/routers/user/setting/oauth2.go b/routers/user/setting/oauth2.go index f42c1123e1..7f0f8db1c8 100644 --- a/routers/user/setting/oauth2.go +++ b/routers/user/setting/oauth2.go @@ -8,11 +8,12 @@ import ( "fmt" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" ) const ( @@ -20,7 +21,8 @@ const ( ) // OAuthApplicationsPost response for adding a oauth2 application -func OAuthApplicationsPost(ctx *context.Context, form auth.EditOAuth2ApplicationForm) { +func OAuthApplicationsPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.EditOAuth2ApplicationForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsApplications"] = true @@ -51,7 +53,8 @@ func OAuthApplicationsPost(ctx *context.Context, form auth.EditOAuth2Application } // OAuthApplicationsEdit response for editing oauth2 application -func OAuthApplicationsEdit(ctx *context.Context, form auth.EditOAuth2ApplicationForm) { +func OAuthApplicationsEdit(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.EditOAuth2ApplicationForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsApplications"] = true diff --git a/routers/user/setting/profile.go b/routers/user/setting/profile.go index c935b56230..7e90a7ccec 100644 --- a/routers/user/setting/profile.go +++ b/routers/user/setting/profile.go @@ -14,12 +14,13 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" "github.com/unknwon/i18n" ) @@ -71,7 +72,8 @@ func HandleUsernameChange(ctx *context.Context, user *models.User, newName strin } // ProfilePost response for change user's profile -func ProfilePost(ctx *context.Context, form auth.UpdateProfileForm) { +func ProfilePost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.UpdateProfileForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsProfile"] = true @@ -123,7 +125,7 @@ func ProfilePost(ctx *context.Context, form auth.UpdateProfileForm) { // UpdateAvatarSetting update user's avatar // FIXME: limit size. -func UpdateAvatarSetting(ctx *context.Context, form auth.AvatarForm, ctxUser *models.User) error { +func UpdateAvatarSetting(ctx *context.Context, form *auth.AvatarForm, ctxUser *models.User) error { ctxUser.UseCustomAvatar = form.Source == auth.AvatarLocal if len(form.Gravatar) > 0 { if form.Avatar != nil { @@ -171,7 +173,8 @@ func UpdateAvatarSetting(ctx *context.Context, form auth.AvatarForm, ctxUser *mo } // AvatarPost response for change user's avatar request -func AvatarPost(ctx *context.Context, form auth.AvatarForm) { +func AvatarPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AvatarForm) if err := UpdateAvatarSetting(ctx, form, ctx.User); err != nil { ctx.Flash.Error(err.Error()) } else { diff --git a/routers/user/setting/security_openid.go b/routers/user/setting/security_openid.go index 6813765f6f..401705608a 100644 --- a/routers/user/setting/security_openid.go +++ b/routers/user/setting/security_openid.go @@ -6,15 +6,17 @@ package setting import ( "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/auth/openid" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" ) // OpenIDPost response for change user's openid -func OpenIDPost(ctx *context.Context, form auth.AddOpenIDForm) { +func OpenIDPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.AddOpenIDForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsSecurity"] = true @@ -70,9 +72,9 @@ func OpenIDPost(ctx *context.Context, form auth.AddOpenIDForm) { } func settingsOpenIDVerify(ctx *context.Context) { - log.Trace("Incoming call to: " + ctx.Req.Request.URL.String()) + log.Trace("Incoming call to: " + ctx.Req.URL.String()) - fullURL := setting.AppURL + ctx.Req.Request.URL.String()[1:] + fullURL := setting.AppURL + ctx.Req.URL.String()[1:] log.Trace("Full URL: " + fullURL) id, err := openid.Verify(fullURL) diff --git a/routers/user/setting/security_twofa.go b/routers/user/setting/security_twofa.go index 3f4c8f6c3f..0dee827cab 100644 --- a/routers/user/setting/security_twofa.go +++ b/routers/user/setting/security_twofa.go @@ -13,10 +13,11 @@ import ( "strings" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" "github.com/pquerna/otp" "github.com/pquerna/otp/totp" @@ -165,7 +166,8 @@ func EnrollTwoFactor(ctx *context.Context) { } // EnrollTwoFactorPost handles enrolling the user into 2FA. -func EnrollTwoFactorPost(ctx *context.Context, form auth.TwoFactorAuthForm) { +func EnrollTwoFactorPost(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.TwoFactorAuthForm) ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsSecurity"] = true diff --git a/routers/user/setting/security_u2f.go b/routers/user/setting/security_u2f.go index 7e32b4aaec..8140c3c04a 100644 --- a/routers/user/setting/security_u2f.go +++ b/routers/user/setting/security_u2f.go @@ -8,16 +8,18 @@ import ( "errors" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/context" + auth "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" "github.com/tstranex/u2f" ) // U2FRegister initializes the u2f registration procedure -func U2FRegister(ctx *context.Context, form auth.U2FRegistrationForm) { +func U2FRegister(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.U2FRegistrationForm) if form.Name == "" { ctx.Error(409) return @@ -55,7 +57,8 @@ func U2FRegister(ctx *context.Context, form auth.U2FRegistrationForm) { } // U2FRegisterPost receives the response of the security key -func U2FRegisterPost(ctx *context.Context, response u2f.RegisterResponse) { +func U2FRegisterPost(ctx *context.Context) { + response := web.GetForm(ctx).(*u2f.RegisterResponse) challSess := ctx.Session.Get("u2fChallenge") u2fName := ctx.Session.Get("u2fName") if challSess == nil || u2fName == nil { @@ -69,7 +72,7 @@ func U2FRegisterPost(ctx *context.Context, response u2f.RegisterResponse) { // certificate by default. SkipAttestationVerify: true, } - reg, err := u2f.Register(response, *challenge, config) + reg, err := u2f.Register(*response, *challenge, config) if err != nil { ctx.ServerError("u2f.Register", err) return @@ -82,7 +85,8 @@ func U2FRegisterPost(ctx *context.Context, response u2f.RegisterResponse) { } // U2FDelete deletes an security key by id -func U2FDelete(ctx *context.Context, form auth.U2FDeleteForm) { +func U2FDelete(ctx *context.Context) { + form := web.GetForm(ctx).(*auth.U2FDeleteForm) reg, err := models.GetU2FRegistrationByID(form.ID) if err != nil { if models.IsErrU2FRegistrationNotExist(err) { -- cgit v1.2.3