aboutsummaryrefslogtreecommitdiffstats
path: root/routers/web/web.go
diff options
context:
space:
mode:
Diffstat (limited to 'routers/web/web.go')
-rw-r--r--routers/web/web.go263
1 files changed, 153 insertions, 110 deletions
diff --git a/routers/web/web.go b/routers/web/web.go
index 609a7d77ac..1039f9e739 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -37,6 +37,7 @@ import (
"code.gitea.io/gitea/routers/web/repo"
"code.gitea.io/gitea/routers/web/repo/actions"
repo_setting "code.gitea.io/gitea/routers/web/repo/setting"
+ shared_actions "code.gitea.io/gitea/routers/web/shared/actions"
"code.gitea.io/gitea/routers/web/shared/project"
"code.gitea.io/gitea/routers/web/user"
user_setting "code.gitea.io/gitea/routers/web/user/setting"
@@ -118,7 +119,7 @@ func webAuth(authMethod auth_service.Method) func(*context.Context) {
ar, err := common.AuthShared(ctx.Base, ctx.Session, authMethod)
if err != nil {
log.Error("Failed to verify user: %v", err)
- ctx.Error(http.StatusUnauthorized, "Verify")
+ ctx.HTTPError(http.StatusUnauthorized, "Failed to authenticate user")
return
}
ctx.Doer = ar.Doer
@@ -153,7 +154,7 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Cont
if ctx.Doer.MustChangePassword {
if ctx.Req.URL.Path != "/user/settings/change_password" {
if strings.HasPrefix(ctx.Req.UserAgent(), "git") {
- ctx.Error(http.StatusUnauthorized, ctx.Locale.TrString("auth.must_change_password"))
+ ctx.HTTPError(http.StatusUnauthorized, ctx.Locale.TrString("auth.must_change_password"))
return
}
ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
@@ -177,7 +178,7 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Cont
return
}
- if !options.SignOutRequired && !options.DisableCSRF && ctx.Req.Method == "POST" {
+ if !options.SignOutRequired && !options.DisableCSRF && ctx.Req.Method == http.MethodPost {
ctx.Csrf.Validate(ctx)
if ctx.Written() {
return
@@ -210,7 +211,7 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Cont
if options.AdminRequired {
if !ctx.Doer.IsAdmin {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
ctx.Data["PageIsAdmin"] = true
@@ -232,8 +233,8 @@ func Routes() *web.Router {
routes.Head("/", misc.DummyOK) // for health check - doesn't need to be passed through gzip handler
routes.Methods("GET, HEAD, OPTIONS", "/assets/*", optionsCorsHandler(), public.FileHandlerFunc())
- routes.Methods("GET, HEAD", "/avatars/*", storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars))
- routes.Methods("GET, HEAD", "/repo-avatars/*", storageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars))
+ routes.Methods("GET, HEAD", "/avatars/*", avatarStorageHandler(setting.Avatar.Storage, "avatars", storage.Avatars))
+ routes.Methods("GET, HEAD", "/repo-avatars/*", avatarStorageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars))
routes.Methods("GET, HEAD", "/apple-touch-icon.png", misc.StaticRedirect("/assets/img/apple-touch-icon.png"))
routes.Methods("GET, HEAD", "/apple-touch-icon-precomposed.png", misc.StaticRedirect("/assets/img/apple-touch-icon.png"))
routes.Methods("GET, HEAD", "/favicon.ico", misc.StaticRedirect("/assets/img/favicon.png"))
@@ -279,62 +280,60 @@ func Routes() *web.Router {
routes.Get("/api/swagger", append(mid, misc.Swagger)...) // Render V1 by default
}
- // TODO: These really seem like things that could be folded into Contexter or as helper functions
- mid = append(mid, user.GetNotificationCount)
- mid = append(mid, repo.GetActiveStopwatch)
mid = append(mid, goGet)
+ mid = append(mid, common.PageTmplFunctions)
- others := web.NewRouter()
- others.Use(mid...)
- registerRoutes(others)
- routes.Mount("", others)
+ webRoutes := web.NewRouter()
+ webRoutes.Use(mid...)
+ webRoutes.Group("", func() { registerWebRoutes(webRoutes) }, common.BlockExpensive(), common.QoS())
+ routes.Mount("", webRoutes)
return routes
}
var optSignInIgnoreCsrf = verifyAuthWithOptions(&common.VerifyOptions{DisableCSRF: true})
-// registerRoutes register routes
-func registerRoutes(m *web.Router) {
+// registerWebRoutes register routes
+func registerWebRoutes(m *web.Router) {
// required to be signed in or signed out
reqSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: true})
reqSignOut := verifyAuthWithOptions(&common.VerifyOptions{SignOutRequired: true})
// optional sign in (if signed in, use the user as doer, if not, no doer)
- optSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInView})
- optExploreSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInView || setting.Service.Explore.RequireSigninView})
+ optSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInViewStrict})
+ optExploreSignIn := verifyAuthWithOptions(&common.VerifyOptions{SignInRequired: setting.Service.RequireSignInViewStrict || setting.Service.Explore.RequireSigninView})
validation.AddBindingRules()
linkAccountEnabled := func(ctx *context.Context) {
if !setting.Service.EnableOpenIDSignIn && !setting.Service.EnableOpenIDSignUp && !setting.OAuth2.Enabled {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
}
openIDSignInEnabled := func(ctx *context.Context) {
if !setting.Service.EnableOpenIDSignIn {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
}
openIDSignUpEnabled := func(ctx *context.Context) {
if !setting.Service.EnableOpenIDSignUp {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
}
oauth2Enabled := func(ctx *context.Context) {
if !setting.OAuth2.Enabled {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
}
reqMilestonesDashboardPageEnabled := func(ctx *context.Context) {
if !setting.Service.ShowMilestonesDashboardPage {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
}
@@ -342,49 +341,56 @@ func registerRoutes(m *web.Router) {
// webhooksEnabled requires webhooks to be enabled by admin.
webhooksEnabled := func(ctx *context.Context) {
if setting.DisableWebhooks {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
+ return
+ }
+ }
+
+ starsEnabled := func(ctx *context.Context) {
+ if setting.Repository.DisableStars {
+ ctx.HTTPError(http.StatusForbidden)
return
}
}
lfsServerEnabled := func(ctx *context.Context) {
if !setting.LFS.StartServer {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
}
federationEnabled := func(ctx *context.Context) {
if !setting.Federation.Enabled {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
}
dlSourceEnabled := func(ctx *context.Context) {
if setting.Repository.DisableDownloadSourceArchives {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
}
sitemapEnabled := func(ctx *context.Context) {
if !setting.Other.EnableSitemap {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
}
packagesEnabled := func(ctx *context.Context) {
if !setting.Packages.Enabled {
- ctx.Error(http.StatusForbidden)
+ ctx.HTTPError(http.StatusForbidden)
return
}
}
feedEnabled := func(ctx *context.Context) {
if !setting.Other.EnableFeed {
- ctx.Error(http.StatusNotFound)
+ ctx.HTTPError(http.StatusNotFound)
return
}
}
@@ -393,18 +399,18 @@ func registerRoutes(m *web.Router) {
return func(ctx *context.Context) {
// only check global disabled units when ignoreGlobal is false
if !ignoreGlobal && unitType.UnitGlobalDisabled() {
- ctx.NotFound("Repo unit is is disabled: "+unitType.LogString(), nil)
+ ctx.NotFound(nil)
return
}
if ctx.ContextUser == nil {
- ctx.NotFound("ContextUser is nil", nil)
+ ctx.NotFound(nil)
return
}
if ctx.ContextUser.IsOrganization() {
if ctx.Org.Organization.UnitPermission(ctx, ctx.Doer, unitType) < accessMode {
- ctx.NotFound("ContextUser is org but doer has no access to unit", nil)
+ ctx.NotFound(nil)
return
}
}
@@ -442,10 +448,10 @@ func registerRoutes(m *web.Router) {
addSettingsVariablesRoutes := func() {
m.Group("/variables", func() {
- m.Get("", repo_setting.Variables)
- m.Post("/new", web.Bind(forms.EditVariableForm{}), repo_setting.VariableCreate)
- m.Post("/{variable_id}/edit", web.Bind(forms.EditVariableForm{}), repo_setting.VariableUpdate)
- m.Post("/{variable_id}/delete", repo_setting.VariableDelete)
+ m.Get("", shared_actions.Variables)
+ m.Post("/new", web.Bind(forms.EditVariableForm{}), shared_actions.VariableCreate)
+ m.Post("/{variable_id}/edit", web.Bind(forms.EditVariableForm{}), shared_actions.VariableUpdate)
+ m.Post("/{variable_id}/delete", shared_actions.VariableDelete)
})
}
@@ -459,11 +465,11 @@ func registerRoutes(m *web.Router) {
addSettingsRunnersRoutes := func() {
m.Group("/runners", func() {
- m.Get("", repo_setting.Runners)
- m.Combo("/{runnerid}").Get(repo_setting.RunnersEdit).
- Post(web.Bind(forms.EditRunnerForm{}), repo_setting.RunnersEditPost)
- m.Post("/{runnerid}/delete", repo_setting.RunnerDeletePost)
- m.Post("/reset_registration_token", repo_setting.ResetRunnerRegistrationToken)
+ m.Get("", shared_actions.Runners)
+ m.Combo("/{runnerid}").Get(shared_actions.RunnersEdit).
+ Post(web.Bind(forms.EditRunnerForm{}), shared_actions.RunnersEditPost)
+ m.Post("/{runnerid}/delete", shared_actions.RunnerDeletePost)
+ m.Post("/reset_registration_token", shared_actions.ResetRunnerRegistrationToken)
})
}
@@ -498,7 +504,7 @@ func registerRoutes(m *web.Router) {
m.Get("/organizations", explore.Organizations)
m.Get("/code", func(ctx *context.Context) {
if unit.TypeCode.UnitGlobalDisabled() {
- ctx.NotFound("Repo unit code is disabled", nil)
+ ctx.NotFound(nil)
return
}
}, explore.Code)
@@ -572,6 +578,7 @@ func registerRoutes(m *web.Router) {
m.Group("/user/settings", func() {
m.Get("", user_setting.Profile)
m.Post("", web.Bind(forms.UpdateProfileForm{}), user_setting.ProfilePost)
+ m.Post("/update_preferences", user_setting.UpdatePreferences)
m.Get("/change_password", auth.MustChangePassword)
m.Post("/change_password", web.Bind(forms.MustChangePasswordForm{}), auth.MustChangePasswordPost)
m.Post("/avatar", web.Bind(forms.AvatarForm{}), user_setting.AvatarPost)
@@ -681,7 +688,7 @@ func registerRoutes(m *web.Router) {
m.Get("/activate", auth.Activate)
m.Post("/activate", auth.ActivatePost)
m.Any("/activate_email", auth.ActivateEmail)
- m.Get("/avatar/{username}/{size}", user.AvatarByUserName)
+ m.Get("/avatar/{username}/{size}", user.AvatarByUsernameSize)
m.Get("/recover_account", auth.ResetPasswd)
m.Post("/recover_account", auth.ResetPasswdPost)
m.Get("/forgot_password", auth.ForgotPasswd)
@@ -720,6 +727,7 @@ func registerRoutes(m *web.Router) {
m.Group("/monitor", func() {
m.Get("/stats", admin.MonitorStats)
m.Get("/cron", admin.CronTasks)
+ m.Get("/perftrace", admin.PerfTrace)
m.Get("/stacktrace", admin.Stacktrace)
m.Post("/stacktrace/cancel/{pid}", admin.StacktraceCancel)
m.Get("/queue", admin.Queues)
@@ -814,7 +822,7 @@ func registerRoutes(m *web.Router) {
m.Methods("GET, OPTIONS", "/attachments/{uuid}", optionsCorsHandler(), repo.GetAttachment)
}, optSignIn)
- m.Post("/{username}", reqSignIn, context.UserAssignmentWeb(), user.Action)
+ m.Post("/{username}", reqSignIn, context.UserAssignmentWeb(), user.ActionUserFollow)
reqRepoAdmin := context.RequireRepoAdmin()
reqRepoCodeWriter := context.RequireUnitWriter(unit.TypeCode)
@@ -838,7 +846,7 @@ func registerRoutes(m *web.Router) {
reqPackageAccess := func(accessMode perm.AccessMode) func(ctx *context.Context) {
return func(ctx *context.Context) {
if ctx.Package.AccessMode < accessMode && !ctx.IsUserSiteAdmin() {
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
}
}
}
@@ -846,15 +854,15 @@ func registerRoutes(m *web.Router) {
individualPermsChecker := func(ctx *context.Context) {
// org permissions have been checked in context.OrgAssignment(), but individual permissions haven't been checked.
if ctx.ContextUser.IsIndividual() {
- switch {
- case ctx.ContextUser.Visibility == structs.VisibleTypePrivate:
+ switch ctx.ContextUser.Visibility {
+ case structs.VisibleTypePrivate:
if ctx.Doer == nil || (ctx.ContextUser.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin) {
- ctx.NotFound("Visit Project", nil)
+ ctx.NotFound(nil)
return
}
- case ctx.ContextUser.Visibility == structs.VisibleTypeLimited:
+ case structs.VisibleTypeLimited:
if ctx.Doer == nil {
- ctx.NotFound("Visit Project", nil)
+ ctx.NotFound(nil)
return
}
}
@@ -864,7 +872,7 @@ func registerRoutes(m *web.Router) {
m.Group("/org", func() {
m.Group("/{org}", func() {
m.Get("/members", org.Members)
- }, context.OrgAssignment())
+ }, context.OrgAssignment(context.OrgAssignmentOptions{}))
}, optSignIn)
// end "/org": members
@@ -890,19 +898,20 @@ func registerRoutes(m *web.Router) {
m.Get("/milestones/{team}", reqMilestonesDashboardPageEnabled, user.Milestones)
m.Post("/members/action/{action}", org.MembersAction)
m.Get("/teams", org.Teams)
- }, context.OrgAssignment(true, false, true))
+ }, context.OrgAssignment(context.OrgAssignmentOptions{RequireMember: true, RequireTeamMember: 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))
+ }, context.OrgAssignment(context.OrgAssignmentOptions{RequireMember: true, RequireTeamMember: true}))
- // require admin permission
+ // require member/team-admin permission (old logic is: requireMember=true, requireTeamAdmin=true)
+ // but it doesn't seem right: requireTeamAdmin does nothing
m.Group("/{org}", func() {
m.Get("/teams/-/search", org.SearchTeam)
- }, context.OrgAssignment(true, false, false, true))
+ }, context.OrgAssignment(context.OrgAssignmentOptions{RequireMember: true, RequireTeamAdmin: true}))
// require owner permission
m.Group("/{org}", func() {
@@ -912,6 +921,8 @@ func registerRoutes(m *web.Router) {
m.Post("/teams/{team}/edit", web.Bind(forms.CreateTeamForm{}), org.EditTeamPost)
m.Post("/teams/{team}/delete", org.DeleteTeam)
+ m.Get("/worktime", context.OrgAssignment(context.OrgAssignmentOptions{RequireOwner: true}), org.Worktime)
+
m.Group("/settings", func() {
m.Combo("").Get(org.Settings).
Post(web.Bind(forms.UpdateOrgSettingForm{}), org.SettingsPost)
@@ -953,7 +964,8 @@ func registerRoutes(m *web.Router) {
addSettingsVariablesRoutes()
}, actions.MustEnableActions)
- m.Methods("GET,POST", "/delete", org.SettingsDelete)
+ m.Post("/rename", web.Bind(forms.RenameOrgForm{}), org.SettingsRenamePost)
+ m.Post("/delete", org.SettingsDeleteOrgPost)
m.Group("/packages", func() {
m.Get("", org.Packages)
@@ -979,7 +991,7 @@ func registerRoutes(m *web.Router) {
m.Post("", web.Bind(forms.BlockUserForm{}), org.BlockedUsersPost)
})
}, ctxDataSet("EnableOAuth2", setting.OAuth2.Enabled, "EnablePackages", setting.Packages.Enabled, "PageIsOrgSettings", true))
- }, context.OrgAssignment(true, true))
+ }, context.OrgAssignment(context.OrgAssignmentOptions{RequireOwner: true}))
}, reqSignIn)
// end "/org": most org routes
@@ -1001,6 +1013,7 @@ func registerRoutes(m *web.Router) {
m.Get("/versions", user.ListPackageVersions)
m.Group("/{version}", func() {
m.Get("", user.ViewPackageVersion)
+ m.Get("/{version_sub}", user.ViewPackageVersion)
m.Get("/files/{fileid}", user.DownloadPackageFile)
m.Group("/settings", func() {
m.Get("", user.PackageSettings)
@@ -1018,7 +1031,7 @@ func registerRoutes(m *web.Router) {
m.Get("", org.Projects)
m.Get("/{id}", org.ViewProject)
}, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead, true))
- m.Group("", func() { //nolint:dupl
+ m.Group("", func() { //nolint:dupl // duplicates lines 1421-1441
m.Get("/new", org.RenderNewProject)
m.Post("/new", web.Bind(forms.CreateProjectForm{}), org.NewProjectPost)
m.Group("/{id}", func() {
@@ -1040,7 +1053,7 @@ func registerRoutes(m *web.Router) {
})
}, reqSignIn, reqUnitAccess(unit.TypeProjects, perm.AccessModeWrite, true), func(ctx *context.Context) {
if ctx.ContextUser.IsIndividual() && ctx.ContextUser.ID != ctx.Doer.ID {
- ctx.NotFound("NewProject", nil)
+ ctx.NotFound(nil)
return
}
})
@@ -1049,7 +1062,7 @@ func registerRoutes(m *web.Router) {
m.Group("", func() {
m.Get("/code", user.CodeSearch)
}, reqUnitAccess(unit.TypeCode, perm.AccessModeRead, false), individualPermsChecker)
- }, optSignIn, context.UserAssignmentWeb(), context.OrgAssignment())
+ }, optSignIn, context.UserAssignmentWeb(), context.OrgAssignment(context.OrgAssignmentOptions{}))
// end "/{username}/-": packages, projects, code
m.Group("/{username}/{reponame}/-", func() {
@@ -1067,6 +1080,8 @@ func registerRoutes(m *web.Router) {
m.Post("/avatar", web.Bind(forms.AvatarForm{}), repo_setting.SettingsAvatar)
m.Post("/avatar/delete", repo_setting.SettingsDeleteAvatar)
+ m.Combo("/public_access").Get(repo_setting.PublicAccess).Post(repo_setting.PublicAccessPost)
+
m.Group("/collaboration", func() {
m.Combo("").Get(repo_setting.Collaboration).Post(repo_setting.CollaborationPost)
m.Post("/access_mode", repo_setting.ChangeCollaborationAccessMode)
@@ -1135,7 +1150,7 @@ func registerRoutes(m *web.Router) {
})
})
m.Group("/actions", func() {
- m.Get("", repo_setting.RedirectToDefaultSetting)
+ m.Get("", shared_actions.RedirectToDefaultSetting)
addSettingsRunnersRoutes()
addSettingsSecretsRoutes()
addSettingsVariablesRoutes()
@@ -1146,12 +1161,12 @@ func registerRoutes(m *web.Router) {
m.Post("/cancel", repo.MigrateCancelPost)
})
},
- reqSignIn, context.RepoAssignment, reqRepoAdmin, context.RepoRef(),
+ reqSignIn, context.RepoAssignment, reqRepoAdmin,
ctxDataSet("PageIsRepoSettings", true, "LFSStartServer", setting.LFS.StartServer),
)
// end "/{username}/{reponame}/settings"
- // user/org home, including rss feeds
+ // user/org home, including rss feeds like "/{username}/{reponame}.rss"
m.Get("/{username}/{reponame}", optSignIn, context.RepoAssignment, context.RepoRefByType(git.RefTypeBranch), repo.SetEditorconfigIfExists, repo.Home)
m.Post("/{username}/{reponame}/markup", optSignIn, context.RepoAssignment, reqUnitsWithMarkdown, web.Bind(structs.MarkupOption{}), misc.Markup)
@@ -1163,10 +1178,16 @@ func registerRoutes(m *web.Router) {
m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.TreeList)
m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.TreeList)
})
+ m.Group("/tree-view", func() {
+ m.Get("/branch/*", context.RepoRefByType(git.RefTypeBranch), repo.TreeViewNodes)
+ m.Get("/tag/*", context.RepoRefByType(git.RefTypeTag), repo.TreeViewNodes)
+ m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.TreeViewNodes)
+ })
m.Get("/compare", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff)
m.Combo("/compare/*", repo.MustBeNotEmpty, repo.SetEditorconfigIfExists).
Get(repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff).
Post(reqSignIn, context.RepoMustNotBeArchived(), reqUnitPullsReader, repo.MustAllowPulls, web.Bind(forms.CreateIssueForm{}), repo.SetWhitespaceBehavior, repo.CompareAndPullRequestPost)
+ m.Get("/pulls/new/*", repo.PullsNewRedirect)
}, optSignIn, context.RepoAssignment, reqUnitCodeReader)
// end "/{username}/{reponame}": repo code: find, compare, list
@@ -1184,16 +1205,17 @@ func registerRoutes(m *web.Router) {
})
})
}
+ // FIXME: many "pulls" requests are sent to "issues" endpoints correctly, so the issue endpoints have to tolerate pull request permissions at the moment
+ m.Group("/{username}/{reponame}/{type:issues}", addIssuesPullsViewRoutes, optSignIn, context.RepoAssignment, context.RequireUnitReader(unit.TypeIssues, unit.TypePullRequests))
+ m.Group("/{username}/{reponame}/{type:pulls}", addIssuesPullsViewRoutes, optSignIn, context.RepoAssignment, reqUnitPullsReader)
+
m.Group("/{username}/{reponame}", func() {
m.Get("/comments/{id}/attachments", repo.GetCommentAttachments)
m.Get("/labels", repo.RetrieveLabelsForList, repo.Labels)
m.Get("/milestones", repo.Milestones)
- m.Get("/milestone/{id}", context.RepoRef(), repo.MilestoneIssuesAndPulls)
+ m.Get("/milestone/{id}", repo.MilestoneIssuesAndPulls)
m.Get("/issues/suggestions", repo.IssueSuggestions)
}, optSignIn, context.RepoAssignment, reqRepoIssuesOrPullsReader) // issue/pull attachments, labels, milestones
-
- m.Group("/{username}/{reponame}/{type:issues}", addIssuesPullsViewRoutes, optSignIn, context.RepoAssignment, reqUnitIssuesReader)
- m.Group("/{username}/{reponame}/{type:pulls}", addIssuesPullsViewRoutes, optSignIn, context.RepoAssignment, reqUnitPullsReader)
// end "/{username}/{reponame}": view milestone, label, issue, pull, etc
m.Group("/{username}/{reponame}/{type:issues}", func() {
@@ -1205,14 +1227,14 @@ func registerRoutes(m *web.Router) {
m.Group("/{username}/{reponame}", func() { // edit issues, pulls, labels, milestones, etc
m.Group("/issues", func() {
m.Group("/new", func() {
- m.Combo("").Get(context.RepoRef(), repo.NewIssue).
+ m.Combo("").Get(repo.NewIssue).
Post(web.Bind(forms.CreateIssueForm{}), repo.NewIssuePost)
- m.Get("/choose", context.RepoRef(), repo.NewIssueChooseTemplate)
+ m.Get("/choose", repo.NewIssueChooseTemplate)
})
m.Get("/search", repo.SearchRepoIssuesJSON)
}, reqUnitIssuesReader)
- addIssuesPullsRoutes := func() {
+ addIssuesPullsUpdateRoutes := func() {
// for "/{username}/{reponame}/issues" or "/{username}/{reponame}/pulls"
m.Group("/{index}", func() {
m.Post("/title", repo.UpdateIssueTitle)
@@ -1231,7 +1253,8 @@ func registerRoutes(m *web.Router) {
m.Post("/add", web.Bind(forms.AddTimeManuallyForm{}), repo.AddTimeManually)
m.Post("/{timeid}/delete", repo.DeleteTime)
m.Group("/stopwatch", func() {
- m.Post("/toggle", repo.IssueStopwatch)
+ m.Post("/start", repo.IssueStartStopwatch)
+ m.Post("/stop", repo.IssueStopStopwatch)
m.Post("/cancel", repo.CancelStopwatch)
})
})
@@ -1255,8 +1278,9 @@ func registerRoutes(m *web.Router) {
m.Delete("/unpin/{index}", reqRepoAdmin, repo.IssueUnpin)
m.Post("/move_pin", reqRepoAdmin, repo.IssuePinMove)
}
- m.Group("/{type:issues}", addIssuesPullsRoutes, reqUnitIssuesReader, context.RepoMustNotBeArchived())
- m.Group("/{type:pulls}", addIssuesPullsRoutes, reqUnitPullsReader, context.RepoMustNotBeArchived())
+ // FIXME: many "pulls" requests are sent to "issues" endpoints incorrectly, so the issue endpoints have to tolerate pull request permissions at the moment
+ m.Group("/{type:issues}", addIssuesPullsUpdateRoutes, context.RequireUnitReader(unit.TypeIssues, unit.TypePullRequests), context.RepoMustNotBeArchived())
+ m.Group("/{type:pulls}", addIssuesPullsUpdateRoutes, reqUnitPullsReader, context.RepoMustNotBeArchived())
m.Group("/comments/{id}", func() {
m.Post("", repo.UpdateCommentContent)
@@ -1269,7 +1293,7 @@ func registerRoutes(m *web.Router) {
m.Post("/edit", web.Bind(forms.CreateLabelForm{}), repo.UpdateLabel)
m.Post("/delete", repo.DeleteLabel)
m.Post("/initialize", web.Bind(forms.InitializeLabelsForm{}), repo.InitializeLabels)
- }, reqRepoIssuesOrPullsWriter, context.RepoRef())
+ }, reqRepoIssuesOrPullsWriter)
m.Group("/milestones", func() {
m.Combo("/new").Get(repo.NewMilestone).
@@ -1278,9 +1302,9 @@ func registerRoutes(m *web.Router) {
m.Post("/{id}/edit", web.Bind(forms.CreateMilestoneForm{}), repo.EditMilestonePost)
m.Post("/{id}/{action}", repo.ChangeMilestoneStatus)
m.Post("/delete", repo.DeleteMilestone)
- }, reqRepoIssuesOrPullsWriter, context.RepoRef())
+ }, reqRepoIssuesOrPullsWriter)
- // FIXME: need to move these routes to the proper place
+ // FIXME: many "pulls" requests are sent to "issues" endpoints incorrectly, need to move these routes to the proper place
m.Group("/issues", func() {
m.Post("/request_review", repo.UpdatePullReviewRequest)
m.Post("/dismiss_review", reqRepoAdmin, web.Bind(forms.DismissReviewForm{}), repo.DismissReview)
@@ -1290,27 +1314,38 @@ func registerRoutes(m *web.Router) {
}, reqSignIn, context.RepoAssignment, context.RepoMustNotBeArchived())
// end "/{username}/{reponame}": create or edit issues, pulls, labels, milestones
- m.Group("/{username}/{reponame}", func() { // repo code
+ m.Group("/{username}/{reponame}", func() { // repo code (at least "code reader")
m.Group("", func() {
m.Group("", func() {
- m.Combo("/_edit/*").Get(repo.EditFile).
- Post(web.Bind(forms.EditRepoFileForm{}), repo.EditFilePost)
- m.Combo("/_new/*").Get(repo.NewFile).
- Post(web.Bind(forms.EditRepoFileForm{}), repo.NewFilePost)
- m.Post("/_preview/*", web.Bind(forms.EditPreviewDiffForm{}), repo.DiffPreviewPost)
- m.Combo("/_delete/*").Get(repo.DeleteFile).
- Post(web.Bind(forms.DeleteRepoFileForm{}), repo.DeleteFilePost)
- m.Combo("/_upload/*", repo.MustBeAbleToUpload).
+ // "GET" requests only need "code reader" permission, "POST" requests need "code writer" permission.
+ // Because reader can "fork and edit"
+ canWriteToBranch := context.CanWriteToBranch()
+ m.Post("/_preview/*", repo.DiffPreviewPost) // read-only, fine with "code reader"
+ m.Post("/_fork/*", repo.ForkToEditPost) // read-only, fork to own repo, fine with "code reader"
+
+ // the path params are used in PrepareCommitFormOptions to construct the correct form action URL
+ m.Combo("/{editor_action:_edit}/*").
+ Get(repo.EditFile).
+ Post(web.Bind(forms.EditRepoFileForm{}), canWriteToBranch, repo.EditFilePost)
+ m.Combo("/{editor_action:_new}/*").
+ Get(repo.EditFile).
+ Post(web.Bind(forms.EditRepoFileForm{}), canWriteToBranch, repo.EditFilePost)
+ m.Combo("/{editor_action:_delete}/*").
+ Get(repo.DeleteFile).
+ Post(web.Bind(forms.DeleteRepoFileForm{}), canWriteToBranch, repo.DeleteFilePost)
+ m.Combo("/{editor_action:_upload}/*", repo.MustBeAbleToUpload).
Get(repo.UploadFile).
- Post(web.Bind(forms.UploadRepoFileForm{}), repo.UploadFilePost)
- m.Combo("/_diffpatch/*").Get(repo.NewDiffPatch).
- Post(web.Bind(forms.EditRepoFileForm{}), repo.NewDiffPatchPost)
- m.Combo("/_cherrypick/{sha:([a-f0-9]{7,64})}/*").Get(repo.CherryPick).
- Post(web.Bind(forms.CherryPickForm{}), repo.CherryPickPost)
- }, context.RepoRefByType(git.RefTypeBranch), context.CanWriteToBranch())
+ Post(web.Bind(forms.UploadRepoFileForm{}), canWriteToBranch, repo.UploadFilePost)
+ m.Combo("/{editor_action:_diffpatch}/*").
+ Get(repo.NewDiffPatch).
+ Post(web.Bind(forms.EditRepoFileForm{}), canWriteToBranch, repo.NewDiffPatchPost)
+ m.Combo("/{editor_action:_cherrypick}/{sha:([a-f0-9]{7,64})}/*").
+ Get(repo.CherryPick).
+ Post(web.Bind(forms.CherryPickForm{}), canWriteToBranch, repo.CherryPickPost)
+ }, context.RepoRefByType(git.RefTypeBranch), repo.WebGitOperationCommonData)
m.Group("", func() {
m.Post("/upload-file", repo.UploadFileToServer)
- m.Post("/upload-remove", web.Bind(forms.RemoveUploadFileForm{}), repo.RemoveUploadFileFromServer)
+ m.Post("/upload-remove", repo.RemoveUploadFileFromServer)
}, repo.MustBeAbleToUpload, reqRepoCodeWriter)
}, repo.MustBeEditable, context.RepoMustNotBeArchived())
@@ -1357,7 +1392,7 @@ func registerRoutes(m *web.Router) {
m.Post("/delete", repo.DeleteRelease)
m.Post("/attachments", repo.UploadReleaseAttachment)
m.Post("/attachments/remove", repo.DeleteAttachment)
- }, reqSignIn, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, context.RepoRef())
+ }, reqSignIn, context.RepoMustNotBeArchived(), reqRepoReleaseWriter)
m.Group("/releases", func() {
m.Get("/edit/*", repo.EditRelease)
m.Post("/edit/*", web.Bind(forms.EditReleaseForm{}), repo.EditReleasePost)
@@ -1383,7 +1418,7 @@ func registerRoutes(m *web.Router) {
m.Group("/{username}/{reponame}/projects", func() {
m.Get("", repo.Projects)
m.Get("/{id}", repo.ViewProject)
- m.Group("", func() { //nolint:dupl
+ m.Group("", func() { //nolint:dupl // duplicates lines 1034-1054
m.Get("/new", repo.RenderNewProject)
m.Post("/new", web.Bind(forms.CreateProjectForm{}), repo.NewProjectPost)
m.Group("/{id}", func() {
@@ -1425,10 +1460,12 @@ func registerRoutes(m *web.Router) {
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
m.Get("/logs", actions.Logs)
})
+ m.Get("/workflow", actions.ViewWorkflowFile)
m.Post("/cancel", reqRepoActionsWriter, actions.Cancel)
m.Post("/approve", reqRepoActionsWriter, actions.Approve)
+ m.Post("/delete", reqRepoActionsWriter, actions.Delete)
m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView)
- m.Delete("/artifacts/{artifact_name}", actions.ArtifactsDeleteView)
+ m.Delete("/artifacts/{artifact_name}", reqRepoActionsWriter, actions.ArtifactsDeleteView)
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
})
m.Group("/workflows/{workflow_name}", func() {
@@ -1470,7 +1507,7 @@ func registerRoutes(m *web.Router) {
})
m.Group("/recent-commits", func() {
m.Get("", repo.RecentCommits)
- m.Get("/data", repo.RecentCommitsData)
+ m.Get("/data", repo.CodeFrequencyData) // "recent-commits" also uses the same data as "code-frequency"
})
}, reqUnitCodeReader)
},
@@ -1485,20 +1522,21 @@ func registerRoutes(m *web.Router) {
m.Get("", repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewIssue)
m.Get(".diff", repo.DownloadPullDiff)
m.Get(".patch", repo.DownloadPullPatch)
+ m.Get("/merge_box", repo.ViewPullMergeBox)
m.Group("/commits", func() {
- m.Get("", context.RepoRef(), repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewPullCommits)
- m.Get("/list", context.RepoRef(), repo.GetPullCommits)
- m.Get("/{sha:[a-f0-9]{7,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForSingleCommit)
+ m.Get("", repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewPullCommits)
+ m.Get("/list", repo.GetPullCommits)
+ m.Get("/{sha:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForSingleCommit)
})
m.Post("/merge", context.RepoMustNotBeArchived(), web.Bind(forms.MergePullRequestForm{}), repo.MergePullRequest)
m.Post("/cancel_auto_merge", context.RepoMustNotBeArchived(), repo.CancelAutoMergePullRequest)
m.Post("/update", repo.UpdatePullRequest)
m.Post("/set_allow_maintainer_edit", web.Bind(forms.UpdateAllowEditsForm{}), repo.SetAllowEdits)
- m.Post("/cleanup", context.RepoMustNotBeArchived(), context.RepoRef(), repo.CleanUpPullRequest)
+ m.Post("/cleanup", context.RepoMustNotBeArchived(), repo.CleanUpPullRequest)
m.Group("/files", func() {
- m.Get("", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForAllCommitsOfPr)
- m.Get("/{sha:[a-f0-9]{7,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesStartingFromCommit)
- m.Get("/{shaFrom:[a-f0-9]{7,40}}..{shaTo:[a-f0-9]{7,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForRange)
+ m.Get("", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForAllCommitsOfPr)
+ m.Get("/{sha:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesStartingFromCommit)
+ m.Get("/{shaFrom:[a-f0-9]{7,40}}..{shaTo:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForRange)
m.Group("/reviews", func() {
m.Get("/new_comment", repo.RenderNewCodeCommentForm)
m.Post("/comments", web.Bind(forms.CodeCommentForm{}), repo.SetShowOutdatedComments, repo.CreateCodeComment)
@@ -1513,7 +1551,7 @@ func registerRoutes(m *web.Router) {
m.Group("/activity_author_data", func() {
m.Get("", repo.ActivityAuthors)
m.Get("/{period}", repo.ActivityAuthors)
- }, context.RepoRef(), repo.MustBeNotEmpty)
+ }, repo.MustBeNotEmpty)
m.Group("/archive", func() {
m.Get("/*", repo.Download)
@@ -1582,18 +1620,22 @@ func registerRoutes(m *web.Router) {
m.Get("/commit/*", context.RepoRefByType(git.RefTypeCommit), repo.Home)
m.Get("/*", context.RepoRefByType(""), repo.Home) // "/*" route is deprecated, and kept for backward compatibility
}, repo.SetEditorconfigIfExists)
+ m.Get("/tree/*", repo.RedirectRepoTreeToSrc) // redirect "/owner/repo/tree/*" requests to "/owner/repo/src/*"
+ m.Get("/blob/*", repo.RedirectRepoBlobToCommit) // redirect "/owner/repo/blob/*" requests to "/owner/repo/src/commit/*"
- m.Get("/forks", context.RepoRef(), repo.Forks)
+ m.Get("/forks", repo.Forks)
m.Get("/commit/{sha:([a-f0-9]{7,64})}.{ext:patch|diff}", repo.MustBeNotEmpty, repo.RawDiff)
m.Post("/lastcommit/*", context.RepoRefByType(git.RefTypeCommit), repo.LastCommit)
}, optSignIn, context.RepoAssignment, reqUnitCodeReader)
// end "/{username}/{reponame}": repo code
m.Group("/{username}/{reponame}", func() {
- m.Get("/stars", repo.Stars)
+ m.Get("/stars", starsEnabled, repo.Stars)
m.Get("/watchers", repo.Watchers)
m.Get("/search", reqUnitCodeReader, repo.Search)
- m.Post("/action/{action}", reqSignIn, repo.Action)
+ m.Post("/action/{action:star|unstar}", reqSignIn, starsEnabled, repo.ActionStar)
+ m.Post("/action/{action:watch|unwatch}", reqSignIn, repo.ActionWatch)
+ m.Post("/action/{action:accept_transfer|reject_transfer}", reqSignIn, repo.ActionTransfer)
}, optSignIn, context.RepoAssignment)
common.AddOwnerRepoGitLFSRoutes(m, optSignInIgnoreCsrf, lfsServerEnabled) // "/{username}/{reponame}/{lfs-paths}": git-lfs support
@@ -1617,14 +1659,15 @@ func registerRoutes(m *web.Router) {
m.Group("/devtest", func() {
m.Any("", devtest.List)
m.Any("/fetch-action-test", devtest.FetchActionTest)
- m.Any("/{sub}", devtest.Tmpl)
+ m.Any("/{sub}", devtest.TmplCommon)
+ m.Get("/repo-action-view/{run}/{job}", devtest.MockActionsView)
m.Post("/actions-mock/runs/{run}/jobs/{job}", web.Bind(actions.ViewRequest{}), devtest.MockActionsRunsJobs)
})
}
m.NotFound(func(w http.ResponseWriter, req *http.Request) {
- ctx := context.GetWebContext(req)
+ ctx := context.GetWebContext(req.Context())
defer routing.RecordFuncInfo(ctx, routing.GetFuncInfo(ctx.NotFound, "WebNotFound"))()
- ctx.NotFound("", nil)
+ ctx.NotFound(nil)
})
}