From 1bfb0a24d843e10d6d95c4319a84980485e584ed Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 9 Jun 2021 07:33:54 +0800 Subject: [PATCH] Refactor routers directory (#15800) * refactor routers directory * move func used for web and api to common * make corsHandler a function to prohibit side efects * rm unused func Co-authored-by: 6543 <6543@obermui.de> --- .golangci.yml | 4 +- cmd/web.go | 8 +- contrib/pr/checkout.go | 3 +- integrations/create_no_session_test.go | 6 +- integrations/integration_test.go | 3 +- integrations/lfs_getobject_test.go | 8 +- routers/api/v1/repo/file.go | 6 +- routers/common/db.go | 39 ++ routers/common/logger.go | 33 ++ routers/common/middleware.go | 76 ++++ routers/common/repo.go | 127 ++++++ routers/home.go | 413 ------------------ routers/init.go | 80 +--- routers/{ => install}/install.go | 13 +- .../{routes/install.go => install/routes.go} | 38 +- routers/install/setting.go | 49 +++ routers/{ => web}/admin/admin.go | 0 routers/{ => web}/admin/admin_test.go | 0 routers/{ => web}/admin/auths.go | 0 routers/{ => web}/admin/emails.go | 0 routers/{ => web}/admin/hooks.go | 0 .../{user/setting => web/admin}/main_test.go | 2 +- routers/{ => web}/admin/notice.go | 0 routers/{ => web}/admin/orgs.go | 4 +- routers/{ => web}/admin/repos.go | 4 +- routers/{ => web}/admin/users.go | 6 +- routers/{ => web}/admin/users_test.go | 0 routers/{routes => web}/base.go | 55 +-- routers/{ => web}/dev/template.go | 0 routers/{ => web}/events/events.go | 2 +- routers/web/explore/code.go | 139 ++++++ routers/web/explore/org.go | 39 ++ routers/web/explore/repo.go | 131 ++++++ routers/web/explore/user.go | 107 +++++ routers/{routes => web}/goget.go | 2 +- routers/web/home.go | 65 +++ routers/{ => web}/metrics.go | 2 +- routers/{ => web}/org/home.go | 0 routers/{ => web}/org/members.go | 0 routers/{ => web}/org/org.go | 0 routers/{ => web}/org/org_labels.go | 0 routers/{ => web}/org/setting.go | 2 +- routers/{ => web}/org/teams.go | 0 routers/{ => web}/repo/activity.go | 0 routers/{ => web}/repo/attachment.go | 3 +- routers/{ => web}/repo/blame.go | 0 routers/{ => web}/repo/branch.go | 0 routers/{ => web}/repo/commit.go | 0 routers/{ => web}/repo/compare.go | 0 routers/{ => web}/repo/download.go | 100 +---- routers/{ => web}/repo/editor.go | 0 routers/{ => web}/repo/editor_test.go | 0 routers/{ => web}/repo/http.go | 0 routers/{ => web}/repo/issue.go | 0 routers/{ => web}/repo/issue_dependency.go | 0 routers/{ => web}/repo/issue_label.go | 0 routers/{ => web}/repo/issue_label_test.go | 0 routers/{ => web}/repo/issue_lock.go | 0 routers/{ => web}/repo/issue_stopwatch.go | 0 routers/{ => web}/repo/issue_test.go | 0 routers/{ => web}/repo/issue_timetrack.go | 0 routers/{ => web}/repo/issue_watch.go | 0 routers/{ => web}/repo/lfs.go | 0 routers/{ => web}/repo/main_test.go | 2 +- routers/{ => web}/repo/middlewares.go | 0 routers/{ => web}/repo/migrate.go | 0 routers/{ => web}/repo/milestone.go | 0 routers/{ => web}/repo/projects.go | 0 routers/{ => web}/repo/projects_test.go | 0 routers/{ => web}/repo/pull.go | 0 routers/{ => web}/repo/pull_review.go | 0 routers/{ => web}/repo/release.go | 0 routers/{ => web}/repo/release_test.go | 0 routers/{ => web}/repo/repo.go | 24 - routers/{ => web}/repo/search.go | 0 routers/{ => web}/repo/setting.go | 0 .../repo/setting_protected_branch.go | 0 routers/{ => web}/repo/settings_test.go | 0 routers/{ => web}/repo/topic.go | 0 routers/{ => web}/repo/view.go | 0 routers/{ => web}/repo/webhook.go | 0 routers/{ => web}/repo/wiki.go | 3 +- routers/{ => web}/repo/wiki_test.go | 0 routers/{ => web}/swagger_json.go | 2 +- routers/{ => web}/user/auth.go | 0 routers/{ => web}/user/auth_openid.go | 0 routers/{ => web}/user/avatar.go | 0 routers/{ => web}/user/home.go | 0 routers/{ => web}/user/home_test.go | 0 routers/{ => web}/user/main_test.go | 2 +- routers/{ => web}/user/notification.go | 0 routers/{ => web}/user/oauth.go | 0 routers/{ => web}/user/profile.go | 2 +- routers/{ => web}/user/setting/account.go | 0 .../{ => web}/user/setting/account_test.go | 0 routers/{ => web}/user/setting/adopt.go | 0 .../{ => web}/user/setting/applications.go | 0 routers/{ => web}/user/setting/keys.go | 0 .../{admin => web/user/setting}/main_test.go | 4 +- routers/{ => web}/user/setting/oauth2.go | 0 routers/{ => web}/user/setting/profile.go | 0 routers/{ => web}/user/setting/security.go | 0 .../{ => web}/user/setting/security_openid.go | 0 .../{ => web}/user/setting/security_twofa.go | 0 .../{ => web}/user/setting/security_u2f.go | 0 routers/{ => web}/user/task.go | 0 routers/{routes => web}/web.go | 132 ++---- 107 files changed, 940 insertions(+), 800 deletions(-) create mode 100644 routers/common/db.go create mode 100644 routers/common/logger.go create mode 100644 routers/common/middleware.go create mode 100644 routers/common/repo.go delete mode 100644 routers/home.go rename routers/{ => install}/install.go (98%) rename routers/{routes/install.go => install/routes.go} (81%) create mode 100644 routers/install/setting.go rename routers/{ => web}/admin/admin.go (100%) rename routers/{ => web}/admin/admin_test.go (100%) rename routers/{ => web}/admin/auths.go (100%) rename routers/{ => web}/admin/emails.go (100%) rename routers/{ => web}/admin/hooks.go (100%) rename routers/{user/setting => web/admin}/main_test.go (95%) rename routers/{ => web}/admin/notice.go (100%) rename routers/{ => web}/admin/orgs.go (90%) rename routers/{ => web}/admin/repos.go (97%) rename routers/{ => web}/admin/users.go (98%) rename routers/{ => web}/admin/users_test.go (100%) rename routers/{routes => web}/base.go (76%) rename routers/{ => web}/dev/template.go (100%) rename routers/{ => web}/events/events.go (98%) create mode 100644 routers/web/explore/code.go create mode 100644 routers/web/explore/org.go create mode 100644 routers/web/explore/repo.go create mode 100644 routers/web/explore/user.go rename routers/{routes => web}/goget.go (99%) create mode 100644 routers/web/home.go rename routers/{ => web}/metrics.go (98%) rename routers/{ => web}/org/home.go (100%) rename routers/{ => web}/org/members.go (100%) rename routers/{ => web}/org/org.go (100%) rename routers/{ => web}/org/org_labels.go (100%) rename routers/{ => web}/org/setting.go (99%) rename routers/{ => web}/org/teams.go (100%) rename routers/{ => web}/repo/activity.go (100%) rename routers/{ => web}/repo/attachment.go (97%) rename routers/{ => web}/repo/blame.go (100%) rename routers/{ => web}/repo/branch.go (100%) rename routers/{ => web}/repo/commit.go (100%) rename routers/{ => web}/repo/compare.go (100%) rename routers/{ => web}/repo/download.go (52%) rename routers/{ => web}/repo/editor.go (100%) rename routers/{ => web}/repo/editor_test.go (100%) rename routers/{ => web}/repo/http.go (100%) rename routers/{ => web}/repo/issue.go (100%) rename routers/{ => web}/repo/issue_dependency.go (100%) rename routers/{ => web}/repo/issue_label.go (100%) rename routers/{ => web}/repo/issue_label_test.go (100%) rename routers/{ => web}/repo/issue_lock.go (100%) rename routers/{ => web}/repo/issue_stopwatch.go (100%) rename routers/{ => web}/repo/issue_test.go (100%) rename routers/{ => web}/repo/issue_timetrack.go (100%) rename routers/{ => web}/repo/issue_watch.go (100%) rename routers/{ => web}/repo/lfs.go (100%) rename routers/{ => web}/repo/main_test.go (84%) rename routers/{ => web}/repo/middlewares.go (100%) rename routers/{ => web}/repo/migrate.go (100%) rename routers/{ => web}/repo/milestone.go (100%) rename routers/{ => web}/repo/projects.go (100%) rename routers/{ => web}/repo/projects_test.go (100%) rename routers/{ => web}/repo/pull.go (100%) rename routers/{ => web}/repo/pull_review.go (100%) rename routers/{ => web}/repo/release.go (100%) rename routers/{ => web}/repo/release_test.go (100%) rename routers/{ => web}/repo/repo.go (95%) rename routers/{ => web}/repo/search.go (100%) rename routers/{ => web}/repo/setting.go (100%) rename routers/{ => web}/repo/setting_protected_branch.go (100%) rename routers/{ => web}/repo/settings_test.go (100%) rename routers/{ => web}/repo/topic.go (100%) rename routers/{ => web}/repo/view.go (100%) rename routers/{ => web}/repo/webhook.go (100%) rename routers/{ => web}/repo/wiki.go (99%) rename routers/{ => web}/repo/wiki_test.go (100%) rename routers/{ => web}/swagger_json.go (97%) rename routers/{ => web}/user/auth.go (100%) rename routers/{ => web}/user/auth_openid.go (100%) rename routers/{ => web}/user/avatar.go (100%) rename routers/{ => web}/user/home.go (100%) rename routers/{ => web}/user/home_test.go (100%) rename routers/{ => web}/user/main_test.go (84%) rename routers/{ => web}/user/notification.go (100%) rename routers/{ => web}/user/oauth.go (100%) rename routers/{ => web}/user/profile.go (99%) rename routers/{ => web}/user/setting/account.go (100%) rename routers/{ => web}/user/setting/account_test.go (100%) rename routers/{ => web}/user/setting/adopt.go (100%) rename routers/{ => web}/user/setting/applications.go (100%) rename routers/{ => web}/user/setting/keys.go (100%) rename routers/{admin => web/user/setting}/main_test.go (78%) rename routers/{ => web}/user/setting/oauth2.go (100%) rename routers/{ => web}/user/setting/profile.go (100%) rename routers/{ => web}/user/setting/security.go (100%) rename routers/{ => web}/user/setting/security_openid.go (100%) rename routers/{ => web}/user/setting/security_twofa.go (100%) rename routers/{ => web}/user/setting/security_u2f.go (100%) rename routers/{ => web}/user/task.go (100%) rename routers/{routes => web}/web.go (92%) diff --git a/.golangci.yml b/.golangci.yml index 0d7f90e263..c3dd47ec29 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -70,9 +70,6 @@ issues: - path: modules/log/ linters: - errcheck - - path: routers/routes/web.go - linters: - - dupl - path: routers/api/v1/repo/issue_subscription.go linters: - dupl @@ -114,3 +111,4 @@ issues: linters: - staticcheck text: "svc.IsAnInteractiveSession is deprecated: Use IsWindowsService instead." + diff --git a/cmd/web.go b/cmd/web.go index 9c7d493339..0ba14ae706 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -17,7 +17,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/routers" - "code.gitea.io/gitea/routers/routes" + "code.gitea.io/gitea/routers/install" context2 "github.com/gorilla/context" "github.com/urfave/cli" @@ -88,7 +88,7 @@ func runWeb(ctx *cli.Context) error { } // Perform pre-initialization - needsInstall := routers.PreInstallInit(graceful.GetManager().HammerContext()) + needsInstall := install.PreloadSettings(graceful.GetManager().HammerContext()) if needsInstall { // Flag for port number in case first time run conflict if ctx.IsSet("port") { @@ -101,7 +101,7 @@ func runWeb(ctx *cli.Context) error { return err } } - c := routes.InstallRoutes() + c := install.Routes() err := listen(c, false) select { case <-graceful.GetManager().IsShutdown(): @@ -134,7 +134,7 @@ func runWeb(ctx *cli.Context) error { } // Set up Chi routes - c := routes.NormalRoutes() + c := routers.NormalRoutes() err := listen(c, true) <-graceful.GetManager().Done() log.Info("PID: %d Gitea Web Finished", os.Getpid()) diff --git a/contrib/pr/checkout.go b/contrib/pr/checkout.go index 9ee692fd35..9ce84f762c 100644 --- a/contrib/pr/checkout.go +++ b/contrib/pr/checkout.go @@ -31,7 +31,6 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers" - "code.gitea.io/gitea/routers/routes" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/config" @@ -116,7 +115,7 @@ func runPR() { //routers.GlobalInit() external.RegisterRenderers() markup.Init() - c := routes.NormalRoutes() + c := routers.NormalRoutes() log.Printf("[PR] Ready for testing !\n") log.Printf("[PR] Login with user1, user2, user3, ... with pass: password\n") diff --git a/integrations/create_no_session_test.go b/integrations/create_no_session_test.go index c864b9c7ae..46f111b6f7 100644 --- a/integrations/create_no_session_test.go +++ b/integrations/create_no_session_test.go @@ -14,7 +14,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/routers/routes" + "code.gitea.io/gitea/routers" "gitea.com/go-chi/session" jsoniter "github.com/json-iterator/go" @@ -58,7 +58,7 @@ func TestSessionFileCreation(t *testing.T) { oldSessionConfig := setting.SessionConfig.ProviderConfig defer func() { setting.SessionConfig.ProviderConfig = oldSessionConfig - c = routes.NormalRoutes() + c = routers.NormalRoutes() }() var config session.Options @@ -84,7 +84,7 @@ func TestSessionFileCreation(t *testing.T) { setting.SessionConfig.ProviderConfig = string(newConfigBytes) - c = routes.NormalRoutes() + c = routers.NormalRoutes() t.Run("NoSessionOnViewIssue", func(t *testing.T) { defer PrintCurrentTest(t)() diff --git a/integrations/integration_test.go b/integrations/integration_test.go index 74227416c4..d755977d1a 100644 --- a/integrations/integration_test.go +++ b/integrations/integration_test.go @@ -34,7 +34,6 @@ import ( "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers" - "code.gitea.io/gitea/routers/routes" "github.com/PuerkitoBio/goquery" jsoniter "github.com/json-iterator/go" @@ -88,7 +87,7 @@ func TestMain(m *testing.M) { defer cancel() initIntegrationTest() - c = routes.NormalRoutes() + c = routers.NormalRoutes() // integration test settings... if setting.Cfg != nil { diff --git a/integrations/lfs_getobject_test.go b/integrations/lfs_getobject_test.go index b7423a2dbe..337a93567a 100644 --- a/integrations/lfs_getobject_test.go +++ b/integrations/lfs_getobject_test.go @@ -15,7 +15,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/routers/routes" + "code.gitea.io/gitea/routers/web" jsoniter "github.com/json-iterator/go" gzipp "github.com/klauspost/compress/gzip" @@ -99,7 +99,7 @@ func TestGetLFSLarge(t *testing.T) { t.Skip() return } - content := make([]byte, routes.GzipMinSize*10) + content := make([]byte, web.GzipMinSize*10) for i := range content { content[i] = byte(i % 256) } @@ -115,7 +115,7 @@ func TestGetLFSGzip(t *testing.T) { t.Skip() return } - b := make([]byte, routes.GzipMinSize*10) + b := make([]byte, web.GzipMinSize*10) for i := range b { b[i] = byte(i % 256) } @@ -136,7 +136,7 @@ func TestGetLFSZip(t *testing.T) { t.Skip() return } - b := make([]byte, routes.GzipMinSize*10) + b := make([]byte, web.GzipMinSize*10) for i := range b { b[i] = byte(i % 256) } diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index 37e02874b4..39a60df33f 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -17,7 +17,7 @@ import ( "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" + "code.gitea.io/gitea/routers/common" ) // GetRawFile get a file by path on a repository @@ -83,7 +83,7 @@ func GetRawFile(ctx *context.APIContext) { } return } - if err = repo.ServeBlob(ctx.Context, blob); err != nil { + if err = common.ServeBlob(ctx.Context, blob); err != nil { ctx.Error(http.StatusInternalServerError, "ServeBlob", err) } } @@ -126,7 +126,7 @@ func GetArchive(ctx *context.APIContext) { ctx.Repo.GitRepo = gitRepo defer gitRepo.Close() - repo.Download(ctx.Context) + common.Download(ctx.Context) } // GetEditorconfig get editor config of a repository diff --git a/routers/common/db.go b/routers/common/db.go new file mode 100644 index 0000000000..069a46f64f --- /dev/null +++ b/routers/common/db.go @@ -0,0 +1,39 @@ +// Copyright 2021 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 common + +import ( + "context" + "fmt" + "time" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/models/migrations" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" +) + +// InitDBEngine In case of problems connecting to DB, retry connection. Eg, PGSQL in Docker Container on Synology +func InitDBEngine(ctx context.Context) (err error) { + log.Info("Beginning ORM engine initialization.") + for i := 0; i < setting.Database.DBConnectRetries; i++ { + select { + case <-ctx.Done(): + return fmt.Errorf("Aborted due to shutdown:\nin retry ORM engine initialization") + default: + } + log.Info("ORM engine initialization attempt #%d/%d...", i+1, setting.Database.DBConnectRetries) + if err = models.NewEngine(ctx, migrations.Migrate); err == nil { + break + } else if i == setting.Database.DBConnectRetries-1 { + return err + } + log.Error("ORM engine initialization attempt #%d/%d failed. Error: %v", i+1, setting.Database.DBConnectRetries, err) + log.Info("Backing off for %d seconds", int64(setting.Database.DBConnectBackoff/time.Second)) + time.Sleep(setting.Database.DBConnectBackoff) + } + models.HasEngine = true + return nil +} diff --git a/routers/common/logger.go b/routers/common/logger.go new file mode 100644 index 0000000000..bc1149543c --- /dev/null +++ b/routers/common/logger.go @@ -0,0 +1,33 @@ +// Copyright 2021 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 common + +import ( + "net/http" + "time" + + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" +) + +// 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))) + }) + } +} diff --git a/routers/common/middleware.go b/routers/common/middleware.go new file mode 100644 index 0000000000..1d96522dd9 --- /dev/null +++ b/routers/common/middleware.go @@ -0,0 +1,76 @@ +// Copyright 2021 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 common + +import ( + "fmt" + "net/http" + "strings" + + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + + "github.com/chi-middleware/proxy" + "github.com/go-chi/chi/middleware" +) + +// Middlewares returns common middlewares +func Middlewares() []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) + }) + }, + } + + if setting.ReverseProxyLimit > 0 { + opt := proxy.NewForwardedHeadersOptions(). + WithForwardLimit(setting.ReverseProxyLimit). + ClearTrustedProxies() + for _, n := range setting.ReverseProxyTrustedProxies { + if !strings.Contains(n, "/") { + opt.AddTrustedProxy(n) + } else { + opt.AddTrustedNetwork(n) + } + } + handlers = append(handlers, proxy.ForwardedHeaders(opt)) + } + + handlers = append(handlers, middleware.StripSlashes) + + if !setting.DisableRouterLog && setting.RouterLogLevel != log.NONE { + if log.GetLogger("router").GetLevel() <= setting.RouterLogLevel { + handlers = append(handlers, LoggerHandler(setting.RouterLogLevel)) + } + } + if setting.EnableAccessLog { + handlers = append(handlers, context.AccessLogger()) + } + + 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) + }) + }) + return handlers +} diff --git a/routers/common/repo.go b/routers/common/repo.go new file mode 100644 index 0000000000..c61b5ec57f --- /dev/null +++ b/routers/common/repo.go @@ -0,0 +1,127 @@ +// Copyright 2021 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 common + +import ( + "fmt" + "io" + "net/http" + "path" + "path/filepath" + "strings" + + "code.gitea.io/gitea/modules/charset" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/httpcache" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/typesniffer" + "code.gitea.io/gitea/services/archiver" +) + +// ServeBlob download a git.Blob +func ServeBlob(ctx *context.Context, blob *git.Blob) error { + if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`) { + return nil + } + + dataRc, err := blob.DataAsync() + if err != nil { + return err + } + defer func() { + if err = dataRc.Close(); err != nil { + log.Error("ServeBlob: Close: %v", err) + } + }() + + return ServeData(ctx, ctx.Repo.TreePath, blob.Size(), dataRc) +} + +// Download an archive of a repository +func Download(ctx *context.Context) { + uri := ctx.Params("*") + aReq := archiver.DeriveRequestFrom(ctx, uri) + + if aReq == nil { + ctx.Error(http.StatusNotFound) + return + } + + downloadName := ctx.Repo.Repository.Name + "-" + aReq.GetArchiveName() + complete := aReq.IsComplete() + if !complete { + aReq = archiver.ArchiveRepository(aReq) + complete = aReq.WaitForCompletion(ctx) + } + + if complete { + ctx.ServeFile(aReq.GetArchivePath(), downloadName) + } else { + ctx.Error(http.StatusNotFound) + } +} + +// ServeData download file from io.Reader +func ServeData(ctx *context.Context, name string, size int64, reader io.Reader) error { + buf := make([]byte, 1024) + n, err := reader.Read(buf) + if err != nil && err != io.EOF { + return err + } + if n >= 0 { + buf = buf[:n] + } + + ctx.Resp.Header().Set("Cache-Control", "public,max-age=86400") + + if size >= 0 { + ctx.Resp.Header().Set("Content-Length", fmt.Sprintf("%d", size)) + } else { + log.Error("ServeData called to serve data: %s with size < 0: %d", name, size) + } + name = path.Base(name) + + // Google Chrome dislike commas in filenames, so let's change it to a space + name = strings.ReplaceAll(name, ",", " ") + + st := typesniffer.DetectContentType(buf) + + if st.IsText() || ctx.QueryBool("render") { + cs, err := charset.DetectEncoding(buf) + if err != nil { + log.Error("Detect raw file %s charset failed: %v, using by default utf-8", name, err) + cs = "utf-8" + } + ctx.Resp.Header().Set("Content-Type", "text/plain; charset="+strings.ToLower(cs)) + } else { + ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition") + + if (st.IsImage() || st.IsPDF()) && (setting.UI.SVG.Enabled || !st.IsSvgImage()) { + ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, name)) + if st.IsSvgImage() { + ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox") + ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff") + ctx.Resp.Header().Set("Content-Type", typesniffer.SvgMimeType) + } + } else { + ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name)) + if setting.MimeTypeMap.Enabled { + fileExtension := strings.ToLower(filepath.Ext(name)) + if mimetype, ok := setting.MimeTypeMap.Map[fileExtension]; ok { + ctx.Resp.Header().Set("Content-Type", mimetype) + } + } + } + } + + _, err = ctx.Resp.Write(buf) + if err != nil { + return err + } + _, err = io.Copy(ctx.Resp, reader) + return err +} diff --git a/routers/home.go b/routers/home.go deleted file mode 100644 index 7eaebc081f..0000000000 --- a/routers/home.go +++ /dev/null @@ -1,413 +0,0 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. -// Copyright 2019 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 routers - -import ( - "bytes" - "net/http" - "strings" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/context" - code_indexer "code.gitea.io/gitea/modules/indexer/code" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/modules/web/middleware" - "code.gitea.io/gitea/routers/user" -) - -const ( - // tplHome home page template - tplHome base.TplName = "home" - // tplExploreRepos explore repositories page template - tplExploreRepos base.TplName = "explore/repos" - // tplExploreUsers explore users page template - tplExploreUsers base.TplName = "explore/users" - // tplExploreOrganizations explore organizations page template - tplExploreOrganizations base.TplName = "explore/organizations" - // tplExploreCode explore code page template - tplExploreCode base.TplName = "explore/code" -) - -// Home render home page -func Home(ctx *context.Context) { - if ctx.IsSigned { - if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm { - ctx.Data["Title"] = ctx.Tr("auth.active_your_account") - ctx.HTML(http.StatusOK, user.TplActivate) - } else if !ctx.User.IsActive || ctx.User.ProhibitLogin { - log.Info("Failed authentication attempt for %s from %s", ctx.User.Name, ctx.RemoteAddr()) - ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") - ctx.HTML(http.StatusOK, "user/auth/prohibit_login") - } else if ctx.User.MustChangePassword { - ctx.Data["Title"] = ctx.Tr("auth.must_change_password") - ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password" - middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI()) - ctx.Redirect(setting.AppSubURL + "/user/settings/change_password") - } else { - user.Dashboard(ctx) - } - return - // Check non-logged users landing page. - } else if setting.LandingPageURL != setting.LandingPageHome { - ctx.Redirect(setting.AppSubURL + string(setting.LandingPageURL)) - return - } - - // Check auto-login. - uname := ctx.GetCookie(setting.CookieUserName) - if len(uname) != 0 { - ctx.Redirect(setting.AppSubURL + "/user/login") - return - } - - ctx.Data["PageIsHome"] = true - ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - ctx.HTML(http.StatusOK, tplHome) -} - -// RepoSearchOptions when calling search repositories -type RepoSearchOptions struct { - OwnerID int64 - Private bool - Restricted bool - PageSize int - TplName base.TplName -} - -var ( - nullByte = []byte{0x00} -) - -func isKeywordValid(keyword string) bool { - return !bytes.Contains([]byte(keyword), nullByte) -} - -// RenderRepoSearch render repositories search page -func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { - page := ctx.QueryInt("page") - if page <= 0 { - page = 1 - } - - var ( - repos []*models.Repository - count int64 - err error - orderBy models.SearchOrderBy - ) - - ctx.Data["SortType"] = ctx.Query("sort") - switch ctx.Query("sort") { - case "newest": - orderBy = models.SearchOrderByNewest - case "oldest": - orderBy = models.SearchOrderByOldest - case "recentupdate": - orderBy = models.SearchOrderByRecentUpdated - case "leastupdate": - orderBy = models.SearchOrderByLeastUpdated - case "reversealphabetically": - orderBy = models.SearchOrderByAlphabeticallyReverse - case "alphabetically": - orderBy = models.SearchOrderByAlphabetically - case "reversesize": - orderBy = models.SearchOrderBySizeReverse - case "size": - orderBy = models.SearchOrderBySize - case "moststars": - orderBy = models.SearchOrderByStarsReverse - case "feweststars": - orderBy = models.SearchOrderByStars - case "mostforks": - orderBy = models.SearchOrderByForksReverse - case "fewestforks": - orderBy = models.SearchOrderByForks - default: - ctx.Data["SortType"] = "recentupdate" - orderBy = models.SearchOrderByRecentUpdated - } - - keyword := strings.Trim(ctx.Query("q"), " ") - topicOnly := ctx.QueryBool("topic") - ctx.Data["TopicOnly"] = topicOnly - - repos, count, err = models.SearchRepository(&models.SearchRepoOptions{ - ListOptions: models.ListOptions{ - Page: page, - PageSize: opts.PageSize, - }, - Actor: ctx.User, - OrderBy: orderBy, - Private: opts.Private, - Keyword: keyword, - OwnerID: opts.OwnerID, - AllPublic: true, - AllLimited: true, - TopicOnly: topicOnly, - IncludeDescription: setting.UI.SearchRepoDescription, - }) - if err != nil { - ctx.ServerError("SearchRepository", err) - return - } - ctx.Data["Keyword"] = keyword - ctx.Data["Total"] = count - ctx.Data["Repos"] = repos - ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - - pager := context.NewPagination(int(count), opts.PageSize, page, 5) - pager.SetDefaultParams(ctx) - pager.AddParam(ctx, "topic", "TopicOnly") - ctx.Data["Page"] = pager - - ctx.HTML(http.StatusOK, opts.TplName) -} - -// ExploreRepos render explore repositories page -func ExploreRepos(ctx *context.Context) { - ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage - ctx.Data["Title"] = ctx.Tr("explore") - ctx.Data["PageIsExplore"] = true - ctx.Data["PageIsExploreRepositories"] = true - ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - - var ownerID int64 - if ctx.User != nil && !ctx.User.IsAdmin { - ownerID = ctx.User.ID - } - - RenderRepoSearch(ctx, &RepoSearchOptions{ - PageSize: setting.UI.ExplorePagingNum, - OwnerID: ownerID, - Private: ctx.User != nil, - TplName: tplExploreRepos, - }) -} - -// RenderUserSearch render user search page -func RenderUserSearch(ctx *context.Context, opts *models.SearchUserOptions, tplName base.TplName) { - opts.Page = ctx.QueryInt("page") - if opts.Page <= 1 { - opts.Page = 1 - } - - var ( - users []*models.User - count int64 - err error - orderBy models.SearchOrderBy - ) - - ctx.Data["SortType"] = ctx.Query("sort") - switch ctx.Query("sort") { - case "newest": - orderBy = models.SearchOrderByIDReverse - case "oldest": - orderBy = models.SearchOrderByID - case "recentupdate": - orderBy = models.SearchOrderByRecentUpdated - case "leastupdate": - orderBy = models.SearchOrderByLeastUpdated - case "reversealphabetically": - orderBy = models.SearchOrderByAlphabeticallyReverse - case "alphabetically": - orderBy = models.SearchOrderByAlphabetically - default: - ctx.Data["SortType"] = "alphabetically" - orderBy = models.SearchOrderByAlphabetically - } - - opts.Keyword = strings.Trim(ctx.Query("q"), " ") - opts.OrderBy = orderBy - if len(opts.Keyword) == 0 || isKeywordValid(opts.Keyword) { - users, count, err = models.SearchUsers(opts) - if err != nil { - ctx.ServerError("SearchUsers", err) - return - } - } - ctx.Data["Keyword"] = opts.Keyword - ctx.Data["Total"] = count - ctx.Data["Users"] = users - ctx.Data["UsersTwoFaStatus"] = models.UserList(users).GetTwoFaStatus() - ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail - ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - - pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5) - pager.SetDefaultParams(ctx) - ctx.Data["Page"] = pager - - ctx.HTML(http.StatusOK, tplName) -} - -// ExploreUsers render explore users page -func ExploreUsers(ctx *context.Context) { - if setting.Service.Explore.DisableUsersPage { - ctx.Redirect(setting.AppSubURL + "/explore/repos") - return - } - ctx.Data["Title"] = ctx.Tr("explore") - ctx.Data["PageIsExplore"] = true - ctx.Data["PageIsExploreUsers"] = true - ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - - RenderUserSearch(ctx, &models.SearchUserOptions{ - Actor: ctx.User, - Type: models.UserTypeIndividual, - ListOptions: models.ListOptions{PageSize: setting.UI.ExplorePagingNum}, - IsActive: util.OptionalBoolTrue, - Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate}, - }, tplExploreUsers) -} - -// ExploreOrganizations render explore organizations page -func ExploreOrganizations(ctx *context.Context) { - ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage - ctx.Data["Title"] = ctx.Tr("explore") - ctx.Data["PageIsExplore"] = true - ctx.Data["PageIsExploreOrganizations"] = true - ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - - visibleTypes := []structs.VisibleType{structs.VisibleTypePublic} - if ctx.User != nil { - visibleTypes = append(visibleTypes, structs.VisibleTypeLimited, structs.VisibleTypePrivate) - } - - RenderUserSearch(ctx, &models.SearchUserOptions{ - Actor: ctx.User, - Type: models.UserTypeOrganization, - ListOptions: models.ListOptions{PageSize: setting.UI.ExplorePagingNum}, - Visible: visibleTypes, - }, tplExploreOrganizations) -} - -// ExploreCode render explore code page -func ExploreCode(ctx *context.Context) { - if !setting.Indexer.RepoIndexerEnabled { - ctx.Redirect(setting.AppSubURL+"/explore", 302) - return - } - - ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage - ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - ctx.Data["Title"] = ctx.Tr("explore") - ctx.Data["PageIsExplore"] = true - ctx.Data["PageIsExploreCode"] = true - - language := strings.TrimSpace(ctx.Query("l")) - keyword := strings.TrimSpace(ctx.Query("q")) - page := ctx.QueryInt("page") - if page <= 0 { - page = 1 - } - - queryType := strings.TrimSpace(ctx.Query("t")) - isMatch := queryType == "match" - - var ( - repoIDs []int64 - err error - isAdmin bool - ) - if ctx.User != nil { - isAdmin = ctx.User.IsAdmin - } - - // guest user or non-admin user - if ctx.User == nil || !isAdmin { - repoIDs, err = models.FindUserAccessibleRepoIDs(ctx.User) - if err != nil { - ctx.ServerError("SearchResults", err) - return - } - } - - var ( - total int - searchResults []*code_indexer.Result - searchResultLanguages []*code_indexer.SearchResultLanguages - ) - - // if non-admin login user, we need check UnitTypeCode at first - if ctx.User != nil && len(repoIDs) > 0 { - repoMaps, err := models.GetRepositoriesMapByIDs(repoIDs) - if err != nil { - ctx.ServerError("SearchResults", err) - return - } - - var rightRepoMap = make(map[int64]*models.Repository, len(repoMaps)) - repoIDs = make([]int64, 0, len(repoMaps)) - for id, repo := range repoMaps { - if repo.CheckUnitUser(ctx.User, models.UnitTypeCode) { - rightRepoMap[id] = repo - repoIDs = append(repoIDs, id) - } - } - - ctx.Data["RepoMaps"] = rightRepoMap - - total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch) - if err != nil { - ctx.ServerError("SearchResults", err) - return - } - // if non-login user or isAdmin, no need to check UnitTypeCode - } else if (ctx.User == nil && len(repoIDs) > 0) || isAdmin { - total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch) - if err != nil { - ctx.ServerError("SearchResults", err) - return - } - - var loadRepoIDs = make([]int64, 0, len(searchResults)) - for _, result := range searchResults { - var find bool - for _, id := range loadRepoIDs { - if id == result.RepoID { - find = true - break - } - } - if !find { - loadRepoIDs = append(loadRepoIDs, result.RepoID) - } - } - - repoMaps, err := models.GetRepositoriesMapByIDs(loadRepoIDs) - if err != nil { - ctx.ServerError("SearchResults", err) - return - } - - ctx.Data["RepoMaps"] = repoMaps - } - - ctx.Data["Keyword"] = keyword - ctx.Data["Language"] = language - ctx.Data["queryType"] = queryType - ctx.Data["SearchResults"] = searchResults - ctx.Data["SearchResultLanguages"] = searchResultLanguages - ctx.Data["RequireHighlightJS"] = true - ctx.Data["PageIsViewCode"] = true - - pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5) - pager.SetDefaultParams(ctx) - pager.AddParam(ctx, "l", "Language") - ctx.Data["Page"] = pager - - ctx.HTML(http.StatusOK, tplExploreCode) -} - -// NotFound render 404 page -func NotFound(ctx *context.Context) { - ctx.Data["Title"] = "Page Not Found" - ctx.NotFound("home.NotFound", nil) -} diff --git a/routers/init.go b/routers/init.go index 220d87a29d..5e2eca439e 100644 --- a/routers/init.go +++ b/routers/init.go @@ -6,12 +6,9 @@ package routers import ( "context" - "fmt" "strings" - "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/models/migrations" "code.gitea.io/gitea/modules/auth/sso" "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/cron" @@ -32,6 +29,11 @@ import ( "code.gitea.io/gitea/modules/svg" "code.gitea.io/gitea/modules/task" "code.gitea.io/gitea/modules/translation" + "code.gitea.io/gitea/modules/web" + apiv1 "code.gitea.io/gitea/routers/api/v1" + "code.gitea.io/gitea/routers/common" + "code.gitea.io/gitea/routers/private" + web_routers "code.gitea.io/gitea/routers/web" "code.gitea.io/gitea/services/mailer" mirror_service "code.gitea.io/gitea/services/mirror" pull_service "code.gitea.io/gitea/services/pull" @@ -63,63 +65,6 @@ func NewServices() { notification.NewContext() } -// In case of problems connecting to DB, retry connection. Eg, PGSQL in Docker Container on Synology -func initDBEngine(ctx context.Context) (err error) { - log.Info("Beginning ORM engine initialization.") - for i := 0; i < setting.Database.DBConnectRetries; i++ { - select { - case <-ctx.Done(): - return fmt.Errorf("Aborted due to shutdown:\nin retry ORM engine initialization") - default: - } - log.Info("ORM engine initialization attempt #%d/%d...", i+1, setting.Database.DBConnectRetries) - if err = models.NewEngine(ctx, migrations.Migrate); err == nil { - break - } else if i == setting.Database.DBConnectRetries-1 { - return err - } - log.Error("ORM engine initialization attempt #%d/%d failed. Error: %v", i+1, setting.Database.DBConnectRetries, err) - log.Info("Backing off for %d seconds", int64(setting.Database.DBConnectBackoff/time.Second)) - time.Sleep(setting.Database.DBConnectBackoff) - } - models.HasEngine = true - return nil -} - -// PreInstallInit preloads the configuration to check if we need to run install -func PreInstallInit(ctx context.Context) bool { - setting.NewContext() - if !setting.InstallLock { - log.Trace("AppPath: %s", setting.AppPath) - log.Trace("AppWorkPath: %s", setting.AppWorkPath) - log.Trace("Custom path: %s", setting.CustomPath) - log.Trace("Log path: %s", setting.LogRootPath) - log.Trace("Preparing to run install page") - translation.InitLocales() - if setting.EnableSQLite3 { - log.Info("SQLite3 Supported") - } - setting.InitDBConfig() - svg.Init() - } - - return !setting.InstallLock -} - -// PostInstallInit rereads the settings and starts up the database -func PostInstallInit(ctx context.Context) { - setting.NewContext() - setting.InitDBConfig() - if setting.InstallLock { - if err := initDBEngine(ctx); err == nil { - log.Info("ORM engine initialization successful!") - } else { - log.Fatal("ORM engine initialization failed: %v", err) - } - svg.Init() - } -} - // GlobalInit is for global configuration reload-able. func GlobalInit(ctx context.Context) { setting.NewContext() @@ -151,7 +96,7 @@ func GlobalInit(ctx context.Context) { } else if setting.Database.UseSQLite3 { log.Fatal("SQLite3 is set in settings but NOT Supported") } - if err := initDBEngine(ctx); err == nil { + if err := common.InitDBEngine(ctx); err == nil { log.Info("ORM engine initialization successful!") } else { log.Fatal("ORM engine initialization failed: %v", err) @@ -193,3 +138,16 @@ func GlobalInit(ctx context.Context) { svg.Init() } + +// NormalRoutes represents non install routes +func NormalRoutes() *web.Route { + r := web.NewRoute() + for _, middle := range common.Middlewares() { + r.Use(middle) + } + + r.Mount("/", web_routers.Routes()) + r.Mount("/api/v1", apiv1.Routes()) + r.Mount("/api/internal", private.Routes()) + return r +} diff --git a/routers/install.go b/routers/install/install.go similarity index 98% rename from routers/install.go rename to routers/install/install.go index 6c460a887d..a7040bccad 100644 --- a/routers/install.go +++ b/routers/install/install.go @@ -1,8 +1,9 @@ // Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2021 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 routers +package install import ( "fmt" @@ -38,8 +39,8 @@ const ( tplPostInstall base.TplName = "post-install" ) -// InstallInit prepare for rendering installation page -func InstallInit(next http.Handler) http.Handler { +// Init prepare for rendering installation page +func Init(next http.Handler) http.Handler { var rnd = templates.HTMLRenderer() return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { @@ -158,8 +159,8 @@ func Install(ctx *context.Context) { ctx.HTML(http.StatusOK, tplInstall) } -// InstallPost response for submit install items -func InstallPost(ctx *context.Context) { +// SubmitInstall response for submit install items +func SubmitInstall(ctx *context.Context) { form := *web.GetForm(ctx).(*forms.InstallForm) var err error ctx.Data["CurDbOption"] = form.DbType @@ -409,7 +410,7 @@ func InstallPost(ctx *context.Context) { } // Re-read settings - PostInstallInit(ctx) + ReloadSettings(ctx) // Create admin account if len(form.AdminName) > 0 { diff --git a/routers/routes/install.go b/routers/install/routes.go similarity index 81% rename from routers/routes/install.go rename to routers/install/routes.go index 0918da1a4b..36130d4b3f 100644 --- a/routers/routes/install.go +++ b/routers/install/routes.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package routes +package install import ( "fmt" @@ -15,12 +15,18 @@ import ( "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web/middleware" - "code.gitea.io/gitea/routers" + "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/services/forms" "gitea.com/go-chi/session" ) +type dataStore map[string]interface{} + +func (d *dataStore) GetData() map[string]interface{} { + return *d +} + func installRecovery() func(next http.Handler) http.Handler { var rnd = templates.HTMLRenderer() return func(next http.Handler) http.Handler { @@ -48,21 +54,19 @@ func installRecovery() func(next http.Handler) http.Handler { lc := middleware.Locale(w, req) var store = dataStore{ - Data: templates.Vars{ - "Language": lc.Language(), - "CurrentURL": setting.AppSubURL + req.URL.RequestURI(), - "i18n": lc, - "SignedUserID": int64(0), - "SignedUserName": "", - }, + "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 + store["ErrorMsg"] = combinedErr } - err = rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store.Data)) + err = rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store)) if err != nil { log.Error("%v", err) } @@ -74,10 +78,10 @@ func installRecovery() func(next http.Handler) http.Handler { } } -// InstallRoutes registers the install routes -func InstallRoutes() *web.Route { +// Routes registers the install routes +func Routes() *web.Route { r := web.NewRoute() - for _, middle := range commonMiddlewares() { + for _, middle := range common.Middlewares() { r.Use(middle) } @@ -99,9 +103,9 @@ func InstallRoutes() *web.Route { })) r.Use(installRecovery()) - r.Use(routers.InstallInit) - r.Get("/", routers.Install) - r.Post("/", web.Bind(forms.InstallForm{}), routers.InstallPost) + r.Use(Init) + r.Get("/", Install) + r.Post("/", web.Bind(forms.InstallForm{}), SubmitInstall) r.NotFound(func(w http.ResponseWriter, req *http.Request) { http.Redirect(w, req, setting.AppURL, http.StatusFound) }) diff --git a/routers/install/setting.go b/routers/install/setting.go new file mode 100644 index 0000000000..50bb6aa355 --- /dev/null +++ b/routers/install/setting.go @@ -0,0 +1,49 @@ +// Copyright 2021 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 install + +import ( + "context" + + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/svg" + "code.gitea.io/gitea/modules/translation" + "code.gitea.io/gitea/routers/common" +) + +// PreloadSettings preloads the configuration to check if we need to run install +func PreloadSettings(ctx context.Context) bool { + setting.NewContext() + if !setting.InstallLock { + log.Trace("AppPath: %s", setting.AppPath) + log.Trace("AppWorkPath: %s", setting.AppWorkPath) + log.Trace("Custom path: %s", setting.CustomPath) + log.Trace("Log path: %s", setting.LogRootPath) + log.Trace("Preparing to run install page") + translation.InitLocales() + if setting.EnableSQLite3 { + log.Info("SQLite3 Supported") + } + setting.InitDBConfig() + svg.Init() + } + + return !setting.InstallLock +} + +// ReloadSettings rereads the settings and starts up the database +func ReloadSettings(ctx context.Context) { + setting.NewContext() + setting.InitDBConfig() + if setting.InstallLock { + if err := common.InitDBEngine(ctx); err == nil { + log.Info("ORM engine initialization successful!") + } else { + log.Fatal("ORM engine initialization failed: %v", err) + } + svg.Init() + } +} diff --git a/routers/admin/admin.go b/routers/web/admin/admin.go similarity index 100% rename from routers/admin/admin.go rename to routers/web/admin/admin.go diff --git a/routers/admin/admin_test.go b/routers/web/admin/admin_test.go similarity index 100% rename from routers/admin/admin_test.go rename to routers/web/admin/admin_test.go diff --git a/routers/admin/auths.go b/routers/web/admin/auths.go similarity index 100% rename from routers/admin/auths.go rename to routers/web/admin/auths.go diff --git a/routers/admin/emails.go b/routers/web/admin/emails.go similarity index 100% rename from routers/admin/emails.go rename to routers/web/admin/emails.go diff --git a/routers/admin/hooks.go b/routers/web/admin/hooks.go similarity index 100% rename from routers/admin/hooks.go rename to routers/web/admin/hooks.go diff --git a/routers/user/setting/main_test.go b/routers/web/admin/main_test.go similarity index 95% rename from routers/user/setting/main_test.go rename to routers/web/admin/main_test.go index d343c02f48..352907c737 100644 --- a/routers/user/setting/main_test.go +++ b/routers/web/admin/main_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package setting +package admin import ( "path/filepath" diff --git a/routers/admin/notice.go b/routers/web/admin/notice.go similarity index 100% rename from routers/admin/notice.go rename to routers/web/admin/notice.go diff --git a/routers/admin/orgs.go b/routers/web/admin/orgs.go similarity index 90% rename from routers/admin/orgs.go rename to routers/web/admin/orgs.go index 627f56eaec..618f945704 100644 --- a/routers/admin/orgs.go +++ b/routers/web/admin/orgs.go @@ -11,7 +11,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/routers" + "code.gitea.io/gitea/routers/web/explore" ) const ( @@ -24,7 +24,7 @@ func Organizations(ctx *context.Context) { ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdminOrganizations"] = true - routers.RenderUserSearch(ctx, &models.SearchUserOptions{ + explore.RenderUserSearch(ctx, &models.SearchUserOptions{ Type: models.UserTypeOrganization, ListOptions: models.ListOptions{ PageSize: setting.UI.Admin.OrgPagingNum, diff --git a/routers/admin/repos.go b/routers/web/admin/repos.go similarity index 97% rename from routers/admin/repos.go rename to routers/web/admin/repos.go index d23f7c3d5a..6128992f5a 100644 --- a/routers/admin/repos.go +++ b/routers/web/admin/repos.go @@ -17,7 +17,7 @@ import ( "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/routers" + "code.gitea.io/gitea/routers/web/explore" repo_service "code.gitea.io/gitea/services/repository" ) @@ -32,7 +32,7 @@ func Repos(ctx *context.Context) { ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdminRepositories"] = true - routers.RenderRepoSearch(ctx, &routers.RepoSearchOptions{ + explore.RenderRepoSearch(ctx, &explore.RepoSearchOptions{ Private: true, PageSize: setting.UI.Admin.RepoPagingNum, TplName: tplRepos, diff --git a/routers/admin/users.go b/routers/web/admin/users.go similarity index 98% rename from routers/admin/users.go rename to routers/web/admin/users.go index a71a11dd8a..1b65795865 100644 --- a/routers/admin/users.go +++ b/routers/web/admin/users.go @@ -18,8 +18,8 @@ import ( "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/routers/web/explore" + router_user_setting "code.gitea.io/gitea/routers/web/user/setting" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/mailer" ) @@ -36,7 +36,7 @@ func Users(ctx *context.Context) { ctx.Data["PageIsAdmin"] = true ctx.Data["PageIsAdminUsers"] = true - routers.RenderUserSearch(ctx, &models.SearchUserOptions{ + explore.RenderUserSearch(ctx, &models.SearchUserOptions{ Type: models.UserTypeIndividual, ListOptions: models.ListOptions{ PageSize: setting.UI.Admin.UserPagingNum, diff --git a/routers/admin/users_test.go b/routers/web/admin/users_test.go similarity index 100% rename from routers/admin/users_test.go rename to routers/web/admin/users_test.go diff --git a/routers/routes/base.go b/routers/web/base.go similarity index 76% rename from routers/routes/base.go rename to routers/web/base.go index 0b784508a7..8a44736434 100644 --- a/routers/routes/base.go +++ b/routers/web/base.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package routes +package web import ( "errors" @@ -13,7 +13,6 @@ import ( "path" "path/filepath" "strings" - "time" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/auth/sso" @@ -28,26 +27,6 @@ import ( "gitea.com/go-chi/session" ) -// 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 { @@ -134,12 +113,10 @@ func storageHandler(storageSetting setting.Storage, prefix string, objStore stor } } -type dataStore struct { - Data map[string]interface{} -} +type dataStore map[string]interface{} func (d *dataStore) GetData() map[string]interface{} { - return d.Data + return *d } // Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so. @@ -165,11 +142,9 @@ func Recovery() func(next http.Handler) http.Handler { var lc = middleware.Locale(w, req) var store = dataStore{ - Data: templates.Vars{ - "Language": lc.Language(), - "CurrentURL": setting.AppSubURL + req.URL.RequestURI(), - "i18n": lc, - }, + "Language": lc.Language(), + "CurrentURL": setting.AppSubURL + req.URL.RequestURI(), + "i18n": lc, } var user *models.User @@ -186,22 +161,22 @@ func Recovery() func(next http.Handler) http.Handler { user = sso.SessionUser(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 + store["IsSigned"] = true + store["SignedUser"] = user + store["SignedUserID"] = user.ID + store["SignedUserName"] = user.Name + store["IsAdmin"] = user.IsAdmin } else { - store.Data["SignedUserID"] = int64(0) - store.Data["SignedUserName"] = "" + store["SignedUserID"] = int64(0) + store["SignedUserName"] = "" } w.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) if !setting.IsProd() { - store.Data["ErrorMsg"] = combinedErr + store["ErrorMsg"] = combinedErr } - err = rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store.Data)) + err = rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store)) if err != nil { log.Error("%v", err) } diff --git a/routers/dev/template.go b/routers/web/dev/template.go similarity index 100% rename from routers/dev/template.go rename to routers/web/dev/template.go diff --git a/routers/events/events.go b/routers/web/events/events.go similarity index 98% rename from routers/events/events.go rename to routers/web/events/events.go index b140bf660c..f9cc274851 100644 --- a/routers/events/events.go +++ b/routers/web/events/events.go @@ -15,7 +15,7 @@ import ( "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/routers/user" + "code.gitea.io/gitea/routers/web/user" jsoniter "github.com/json-iterator/go" ) diff --git a/routers/web/explore/code.go b/routers/web/explore/code.go new file mode 100644 index 0000000000..bf15b93cff --- /dev/null +++ b/routers/web/explore/code.go @@ -0,0 +1,139 @@ +// Copyright 2021 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 explore + +import ( + "net/http" + "strings" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + code_indexer "code.gitea.io/gitea/modules/indexer/code" + "code.gitea.io/gitea/modules/setting" +) + +const ( + // tplExploreCode explore code page template + tplExploreCode base.TplName = "explore/code" +) + +// Code render explore code page +func Code(ctx *context.Context) { + if !setting.Indexer.RepoIndexerEnabled { + ctx.Redirect(setting.AppSubURL+"/explore", 302) + return + } + + ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage + ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled + ctx.Data["Title"] = ctx.Tr("explore") + ctx.Data["PageIsExplore"] = true + ctx.Data["PageIsExploreCode"] = true + + language := strings.TrimSpace(ctx.Query("l")) + keyword := strings.TrimSpace(ctx.Query("q")) + page := ctx.QueryInt("page") + if page <= 0 { + page = 1 + } + + queryType := strings.TrimSpace(ctx.Query("t")) + isMatch := queryType == "match" + + var ( + repoIDs []int64 + err error + isAdmin bool + ) + if ctx.User != nil { + isAdmin = ctx.User.IsAdmin + } + + // guest user or non-admin user + if ctx.User == nil || !isAdmin { + repoIDs, err = models.FindUserAccessibleRepoIDs(ctx.User) + if err != nil { + ctx.ServerError("SearchResults", err) + return + } + } + + var ( + total int + searchResults []*code_indexer.Result + searchResultLanguages []*code_indexer.SearchResultLanguages + ) + + // if non-admin login user, we need check UnitTypeCode at first + if ctx.User != nil && len(repoIDs) > 0 { + repoMaps, err := models.GetRepositoriesMapByIDs(repoIDs) + if err != nil { + ctx.ServerError("SearchResults", err) + return + } + + var rightRepoMap = make(map[int64]*models.Repository, len(repoMaps)) + repoIDs = make([]int64, 0, len(repoMaps)) + for id, repo := range repoMaps { + if repo.CheckUnitUser(ctx.User, models.UnitTypeCode) { + rightRepoMap[id] = repo + repoIDs = append(repoIDs, id) + } + } + + ctx.Data["RepoMaps"] = rightRepoMap + + total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch) + if err != nil { + ctx.ServerError("SearchResults", err) + return + } + // if non-login user or isAdmin, no need to check UnitTypeCode + } else if (ctx.User == nil && len(repoIDs) > 0) || isAdmin { + total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch) + if err != nil { + ctx.ServerError("SearchResults", err) + return + } + + var loadRepoIDs = make([]int64, 0, len(searchResults)) + for _, result := range searchResults { + var find bool + for _, id := range loadRepoIDs { + if id == result.RepoID { + find = true + break + } + } + if !find { + loadRepoIDs = append(loadRepoIDs, result.RepoID) + } + } + + repoMaps, err := models.GetRepositoriesMapByIDs(loadRepoIDs) + if err != nil { + ctx.ServerError("SearchResults", err) + return + } + + ctx.Data["RepoMaps"] = repoMaps + } + + ctx.Data["Keyword"] = keyword + ctx.Data["Language"] = language + ctx.Data["queryType"] = queryType + ctx.Data["SearchResults"] = searchResults + ctx.Data["SearchResultLanguages"] = searchResultLanguages + ctx.Data["RequireHighlightJS"] = true + ctx.Data["PageIsViewCode"] = true + + pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5) + pager.SetDefaultParams(ctx) + pager.AddParam(ctx, "l", "Language") + ctx.Data["Page"] = pager + + ctx.HTML(http.StatusOK, tplExploreCode) +} diff --git a/routers/web/explore/org.go b/routers/web/explore/org.go new file mode 100644 index 0000000000..470e0eb853 --- /dev/null +++ b/routers/web/explore/org.go @@ -0,0 +1,39 @@ +// Copyright 2021 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 explore + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" +) + +const ( + // tplExploreOrganizations explore organizations page template + tplExploreOrganizations base.TplName = "explore/organizations" +) + +// Organizations render explore organizations page +func Organizations(ctx *context.Context) { + ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage + ctx.Data["Title"] = ctx.Tr("explore") + ctx.Data["PageIsExplore"] = true + ctx.Data["PageIsExploreOrganizations"] = true + ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled + + visibleTypes := []structs.VisibleType{structs.VisibleTypePublic} + if ctx.User != nil { + visibleTypes = append(visibleTypes, structs.VisibleTypeLimited, structs.VisibleTypePrivate) + } + + RenderUserSearch(ctx, &models.SearchUserOptions{ + Actor: ctx.User, + Type: models.UserTypeOrganization, + ListOptions: models.ListOptions{PageSize: setting.UI.ExplorePagingNum}, + Visible: visibleTypes, + }, tplExploreOrganizations) +} diff --git a/routers/web/explore/repo.go b/routers/web/explore/repo.go new file mode 100644 index 0000000000..e9efae5688 --- /dev/null +++ b/routers/web/explore/repo.go @@ -0,0 +1,131 @@ +// Copyright 2021 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 explore + +import ( + "net/http" + "strings" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/setting" +) + +const ( + // tplExploreRepos explore repositories page template + tplExploreRepos base.TplName = "explore/repos" +) + +// RepoSearchOptions when calling search repositories +type RepoSearchOptions struct { + OwnerID int64 + Private bool + Restricted bool + PageSize int + TplName base.TplName +} + +// RenderRepoSearch render repositories search page +func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { + page := ctx.QueryInt("page") + if page <= 0 { + page = 1 + } + + var ( + repos []*models.Repository + count int64 + err error + orderBy models.SearchOrderBy + ) + + ctx.Data["SortType"] = ctx.Query("sort") + switch ctx.Query("sort") { + case "newest": + orderBy = models.SearchOrderByNewest + case "oldest": + orderBy = models.SearchOrderByOldest + case "recentupdate": + orderBy = models.SearchOrderByRecentUpdated + case "leastupdate": + orderBy = models.SearchOrderByLeastUpdated + case "reversealphabetically": + orderBy = models.SearchOrderByAlphabeticallyReverse + case "alphabetically": + orderBy = models.SearchOrderByAlphabetically + case "reversesize": + orderBy = models.SearchOrderBySizeReverse + case "size": + orderBy = models.SearchOrderBySize + case "moststars": + orderBy = models.SearchOrderByStarsReverse + case "feweststars": + orderBy = models.SearchOrderByStars + case "mostforks": + orderBy = models.SearchOrderByForksReverse + case "fewestforks": + orderBy = models.SearchOrderByForks + default: + ctx.Data["SortType"] = "recentupdate" + orderBy = models.SearchOrderByRecentUpdated + } + + keyword := strings.Trim(ctx.Query("q"), " ") + topicOnly := ctx.QueryBool("topic") + ctx.Data["TopicOnly"] = topicOnly + + repos, count, err = models.SearchRepository(&models.SearchRepoOptions{ + ListOptions: models.ListOptions{ + Page: page, + PageSize: opts.PageSize, + }, + Actor: ctx.User, + OrderBy: orderBy, + Private: opts.Private, + Keyword: keyword, + OwnerID: opts.OwnerID, + AllPublic: true, + AllLimited: true, + TopicOnly: topicOnly, + IncludeDescription: setting.UI.SearchRepoDescription, + }) + if err != nil { + ctx.ServerError("SearchRepository", err) + return + } + ctx.Data["Keyword"] = keyword + ctx.Data["Total"] = count + ctx.Data["Repos"] = repos + ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled + + pager := context.NewPagination(int(count), opts.PageSize, page, 5) + pager.SetDefaultParams(ctx) + pager.AddParam(ctx, "topic", "TopicOnly") + ctx.Data["Page"] = pager + + ctx.HTML(http.StatusOK, opts.TplName) +} + +// Repos render explore repositories page +func Repos(ctx *context.Context) { + ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage + ctx.Data["Title"] = ctx.Tr("explore") + ctx.Data["PageIsExplore"] = true + ctx.Data["PageIsExploreRepositories"] = true + ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled + + var ownerID int64 + if ctx.User != nil && !ctx.User.IsAdmin { + ownerID = ctx.User.ID + } + + RenderRepoSearch(ctx, &RepoSearchOptions{ + PageSize: setting.UI.ExplorePagingNum, + OwnerID: ownerID, + Private: ctx.User != nil, + TplName: tplExploreRepos, + }) +} diff --git a/routers/web/explore/user.go b/routers/web/explore/user.go new file mode 100644 index 0000000000..52f543fe66 --- /dev/null +++ b/routers/web/explore/user.go @@ -0,0 +1,107 @@ +// Copyright 2021 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 explore + +import ( + "bytes" + "net/http" + "strings" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" +) + +const ( + // tplExploreUsers explore users page template + tplExploreUsers base.TplName = "explore/users" +) + +var ( + nullByte = []byte{0x00} +) + +func isKeywordValid(keyword string) bool { + return !bytes.Contains([]byte(keyword), nullByte) +} + +// RenderUserSearch render user search page +func RenderUserSearch(ctx *context.Context, opts *models.SearchUserOptions, tplName base.TplName) { + opts.Page = ctx.QueryInt("page") + if opts.Page <= 1 { + opts.Page = 1 + } + + var ( + users []*models.User + count int64 + err error + orderBy models.SearchOrderBy + ) + + ctx.Data["SortType"] = ctx.Query("sort") + switch ctx.Query("sort") { + case "newest": + orderBy = models.SearchOrderByIDReverse + case "oldest": + orderBy = models.SearchOrderByID + case "recentupdate": + orderBy = models.SearchOrderByRecentUpdated + case "leastupdate": + orderBy = models.SearchOrderByLeastUpdated + case "reversealphabetically": + orderBy = models.SearchOrderByAlphabeticallyReverse + case "alphabetically": + orderBy = models.SearchOrderByAlphabetically + default: + ctx.Data["SortType"] = "alphabetically" + orderBy = models.SearchOrderByAlphabetically + } + + opts.Keyword = strings.Trim(ctx.Query("q"), " ") + opts.OrderBy = orderBy + if len(opts.Keyword) == 0 || isKeywordValid(opts.Keyword) { + users, count, err = models.SearchUsers(opts) + if err != nil { + ctx.ServerError("SearchUsers", err) + return + } + } + ctx.Data["Keyword"] = opts.Keyword + ctx.Data["Total"] = count + ctx.Data["Users"] = users + ctx.Data["UsersTwoFaStatus"] = models.UserList(users).GetTwoFaStatus() + ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail + ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled + + pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5) + pager.SetDefaultParams(ctx) + ctx.Data["Page"] = pager + + ctx.HTML(http.StatusOK, tplName) +} + +// Users render explore users page +func Users(ctx *context.Context) { + if setting.Service.Explore.DisableUsersPage { + ctx.Redirect(setting.AppSubURL + "/explore/repos") + return + } + ctx.Data["Title"] = ctx.Tr("explore") + ctx.Data["PageIsExplore"] = true + ctx.Data["PageIsExploreUsers"] = true + ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled + + RenderUserSearch(ctx, &models.SearchUserOptions{ + Actor: ctx.User, + Type: models.UserTypeIndividual, + ListOptions: models.ListOptions{PageSize: setting.UI.ExplorePagingNum}, + IsActive: util.OptionalBoolTrue, + Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate}, + }, tplExploreUsers) +} diff --git a/routers/routes/goget.go b/routers/web/goget.go similarity index 99% rename from routers/routes/goget.go rename to routers/web/goget.go index 518f5e3073..77934e7f55 100644 --- a/routers/routes/goget.go +++ b/routers/web/goget.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package routes +package web import ( "net/http" diff --git a/routers/web/home.go b/routers/web/home.go new file mode 100644 index 0000000000..f50197691f --- /dev/null +++ b/routers/web/home.go @@ -0,0 +1,65 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2019 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 web + +import ( + "net/http" + + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/routers/web/user" +) + +const ( + // tplHome home page template + tplHome base.TplName = "home" +) + +// Home render home page +func Home(ctx *context.Context) { + if ctx.IsSigned { + if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm { + ctx.Data["Title"] = ctx.Tr("auth.active_your_account") + ctx.HTML(http.StatusOK, user.TplActivate) + } else if !ctx.User.IsActive || ctx.User.ProhibitLogin { + log.Info("Failed authentication attempt for %s from %s", ctx.User.Name, ctx.RemoteAddr()) + ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") + ctx.HTML(http.StatusOK, "user/auth/prohibit_login") + } else if ctx.User.MustChangePassword { + ctx.Data["Title"] = ctx.Tr("auth.must_change_password") + ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password" + middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI()) + ctx.Redirect(setting.AppSubURL + "/user/settings/change_password") + } else { + user.Dashboard(ctx) + } + return + // Check non-logged users landing page. + } else if setting.LandingPageURL != setting.LandingPageHome { + ctx.Redirect(setting.AppSubURL + string(setting.LandingPageURL)) + return + } + + // Check auto-login. + uname := ctx.GetCookie(setting.CookieUserName) + if len(uname) != 0 { + ctx.Redirect(setting.AppSubURL + "/user/login") + return + } + + ctx.Data["PageIsHome"] = true + ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled + ctx.HTML(http.StatusOK, tplHome) +} + +// NotFound render 404 page +func NotFound(ctx *context.Context) { + ctx.Data["Title"] = "Page Not Found" + ctx.NotFound("home.NotFound", nil) +} diff --git a/routers/metrics.go b/routers/web/metrics.go similarity index 98% rename from routers/metrics.go rename to routers/web/metrics.go index db2fb8de44..37558ee337 100644 --- a/routers/metrics.go +++ b/routers/web/metrics.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package routers +package web import ( "crypto/subtle" diff --git a/routers/org/home.go b/routers/web/org/home.go similarity index 100% rename from routers/org/home.go rename to routers/web/org/home.go diff --git a/routers/org/members.go b/routers/web/org/members.go similarity index 100% rename from routers/org/members.go rename to routers/web/org/members.go diff --git a/routers/org/org.go b/routers/web/org/org.go similarity index 100% rename from routers/org/org.go rename to routers/web/org/org.go diff --git a/routers/org/org_labels.go b/routers/web/org/org_labels.go similarity index 100% rename from routers/org/org_labels.go rename to routers/web/org/org_labels.go diff --git a/routers/org/setting.go b/routers/web/org/setting.go similarity index 99% rename from routers/org/setting.go rename to routers/web/org/setting.go index 0e28a93ace..aed90c66f7 100644 --- a/routers/org/setting.go +++ b/routers/web/org/setting.go @@ -15,7 +15,7 @@ import ( "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" + userSetting "code.gitea.io/gitea/routers/web/user/setting" "code.gitea.io/gitea/services/forms" ) diff --git a/routers/org/teams.go b/routers/web/org/teams.go similarity index 100% rename from routers/org/teams.go rename to routers/web/org/teams.go diff --git a/routers/repo/activity.go b/routers/web/repo/activity.go similarity index 100% rename from routers/repo/activity.go rename to routers/web/repo/activity.go diff --git a/routers/repo/attachment.go b/routers/web/repo/attachment.go similarity index 97% rename from routers/repo/attachment.go rename to routers/web/repo/attachment.go index f53e7450ae..5becbea271 100644 --- a/routers/repo/attachment.go +++ b/routers/web/repo/attachment.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/upload" + "code.gitea.io/gitea/routers/common" ) // UploadIssueAttachment response for Issue/PR attachments @@ -152,7 +153,7 @@ func GetAttachment(ctx *context.Context) { } defer fr.Close() - if err = ServeData(ctx, attach.Name, attach.Size, fr); err != nil { + if err = common.ServeData(ctx, attach.Name, attach.Size, fr); err != nil { ctx.ServerError("ServeData", err) return } diff --git a/routers/repo/blame.go b/routers/web/repo/blame.go similarity index 100% rename from routers/repo/blame.go rename to routers/web/repo/blame.go diff --git a/routers/repo/branch.go b/routers/web/repo/branch.go similarity index 100% rename from routers/repo/branch.go rename to routers/web/repo/branch.go diff --git a/routers/repo/commit.go b/routers/web/repo/commit.go similarity index 100% rename from routers/repo/commit.go rename to routers/web/repo/commit.go diff --git a/routers/repo/compare.go b/routers/web/repo/compare.go similarity index 100% rename from routers/repo/compare.go rename to routers/web/repo/compare.go diff --git a/routers/repo/download.go b/routers/web/repo/download.go similarity index 52% rename from routers/repo/download.go rename to routers/web/repo/download.go index bbf4684b2e..6f43d4b839 100644 --- a/routers/repo/download.go +++ b/routers/web/repo/download.go @@ -6,102 +6,14 @@ package repo import ( - "fmt" - "io" - "path" - "path/filepath" - "strings" - - "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/typesniffer" + "code.gitea.io/gitea/routers/common" ) -// ServeData download file from io.Reader -func ServeData(ctx *context.Context, name string, size int64, reader io.Reader) error { - buf := make([]byte, 1024) - n, err := reader.Read(buf) - if err != nil && err != io.EOF { - return err - } - if n >= 0 { - buf = buf[:n] - } - - ctx.Resp.Header().Set("Cache-Control", "public,max-age=86400") - - if size >= 0 { - ctx.Resp.Header().Set("Content-Length", fmt.Sprintf("%d", size)) - } else { - log.Error("ServeData called to serve data: %s with size < 0: %d", name, size) - } - name = path.Base(name) - - // Google Chrome dislike commas in filenames, so let's change it to a space - name = strings.ReplaceAll(name, ",", " ") - - st := typesniffer.DetectContentType(buf) - - if st.IsText() || ctx.QueryBool("render") { - cs, err := charset.DetectEncoding(buf) - if err != nil { - log.Error("Detect raw file %s charset failed: %v, using by default utf-8", name, err) - cs = "utf-8" - } - ctx.Resp.Header().Set("Content-Type", "text/plain; charset="+strings.ToLower(cs)) - } else { - ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition") - - if (st.IsImage() || st.IsPDF()) && (setting.UI.SVG.Enabled || !st.IsSvgImage()) { - ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, name)) - if st.IsSvgImage() { - ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox") - ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff") - ctx.Resp.Header().Set("Content-Type", typesniffer.SvgMimeType) - } - } else { - ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name)) - if setting.MimeTypeMap.Enabled { - fileExtension := strings.ToLower(filepath.Ext(name)) - if mimetype, ok := setting.MimeTypeMap.Map[fileExtension]; ok { - ctx.Resp.Header().Set("Content-Type", mimetype) - } - } - } - } - - _, err = ctx.Resp.Write(buf) - if err != nil { - return err - } - _, err = io.Copy(ctx.Resp, reader) - return err -} - -// ServeBlob download a git.Blob -func ServeBlob(ctx *context.Context, blob *git.Blob) error { - if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`) { - return nil - } - - dataRc, err := blob.DataAsync() - if err != nil { - return err - } - defer func() { - if err = dataRc.Close(); err != nil { - log.Error("ServeBlob: Close: %v", err) - } - }() - - return ServeData(ctx, ctx.Repo.TreePath, blob.Size(), dataRc) -} - // ServeBlobOrLFS download a git.Blob redirecting to LFS if necessary func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob) error { if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`) { @@ -130,7 +42,7 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob) error { log.Error("ServeBlobOrLFS: Close: %v", err) } closed = true - return ServeBlob(ctx, blob) + return common.ServeBlob(ctx, blob) } if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+pointer.Oid+`"`) { return nil @@ -144,14 +56,14 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob) error { log.Error("ServeBlobOrLFS: Close: %v", err) } }() - return ServeData(ctx, ctx.Repo.TreePath, meta.Size, lfsDataRc) + return common.ServeData(ctx, ctx.Repo.TreePath, meta.Size, lfsDataRc) } if err = dataRc.Close(); err != nil { log.Error("ServeBlobOrLFS: Close: %v", err) } closed = true - return ServeBlob(ctx, blob) + return common.ServeBlob(ctx, blob) } // SingleDownload download a file by repos path @@ -165,7 +77,7 @@ func SingleDownload(ctx *context.Context) { } return } - if err = ServeBlob(ctx, blob); err != nil { + if err = common.ServeBlob(ctx, blob); err != nil { ctx.ServerError("ServeBlob", err) } } @@ -197,7 +109,7 @@ func DownloadByID(ctx *context.Context) { } return } - if err = ServeBlob(ctx, blob); err != nil { + if err = common.ServeBlob(ctx, blob); err != nil { ctx.ServerError("ServeBlob", err) } } diff --git a/routers/repo/editor.go b/routers/web/repo/editor.go similarity index 100% rename from routers/repo/editor.go rename to routers/web/repo/editor.go diff --git a/routers/repo/editor_test.go b/routers/web/repo/editor_test.go similarity index 100% rename from routers/repo/editor_test.go rename to routers/web/repo/editor_test.go diff --git a/routers/repo/http.go b/routers/web/repo/http.go similarity index 100% rename from routers/repo/http.go rename to routers/web/repo/http.go diff --git a/routers/repo/issue.go b/routers/web/repo/issue.go similarity index 100% rename from routers/repo/issue.go rename to routers/web/repo/issue.go diff --git a/routers/repo/issue_dependency.go b/routers/web/repo/issue_dependency.go similarity index 100% rename from routers/repo/issue_dependency.go rename to routers/web/repo/issue_dependency.go diff --git a/routers/repo/issue_label.go b/routers/web/repo/issue_label.go similarity index 100% rename from routers/repo/issue_label.go rename to routers/web/repo/issue_label.go diff --git a/routers/repo/issue_label_test.go b/routers/web/repo/issue_label_test.go similarity index 100% rename from routers/repo/issue_label_test.go rename to routers/web/repo/issue_label_test.go diff --git a/routers/repo/issue_lock.go b/routers/web/repo/issue_lock.go similarity index 100% rename from routers/repo/issue_lock.go rename to routers/web/repo/issue_lock.go diff --git a/routers/repo/issue_stopwatch.go b/routers/web/repo/issue_stopwatch.go similarity index 100% rename from routers/repo/issue_stopwatch.go rename to routers/web/repo/issue_stopwatch.go diff --git a/routers/repo/issue_test.go b/routers/web/repo/issue_test.go similarity index 100% rename from routers/repo/issue_test.go rename to routers/web/repo/issue_test.go diff --git a/routers/repo/issue_timetrack.go b/routers/web/repo/issue_timetrack.go similarity index 100% rename from routers/repo/issue_timetrack.go rename to routers/web/repo/issue_timetrack.go diff --git a/routers/repo/issue_watch.go b/routers/web/repo/issue_watch.go similarity index 100% rename from routers/repo/issue_watch.go rename to routers/web/repo/issue_watch.go diff --git a/routers/repo/lfs.go b/routers/web/repo/lfs.go similarity index 100% rename from routers/repo/lfs.go rename to routers/web/repo/lfs.go diff --git a/routers/repo/main_test.go b/routers/web/repo/main_test.go similarity index 84% rename from routers/repo/main_test.go rename to routers/web/repo/main_test.go index 04bbeeb211..47f266365f 100644 --- a/routers/repo/main_test.go +++ b/routers/web/repo/main_test.go @@ -12,5 +12,5 @@ import ( ) func TestMain(m *testing.M) { - models.MainTest(m, filepath.Join("..", "..")) + models.MainTest(m, filepath.Join("..", "..", "..")) } diff --git a/routers/repo/middlewares.go b/routers/web/repo/middlewares.go similarity index 100% rename from routers/repo/middlewares.go rename to routers/web/repo/middlewares.go diff --git a/routers/repo/migrate.go b/routers/web/repo/migrate.go similarity index 100% rename from routers/repo/migrate.go rename to routers/web/repo/migrate.go diff --git a/routers/repo/milestone.go b/routers/web/repo/milestone.go similarity index 100% rename from routers/repo/milestone.go rename to routers/web/repo/milestone.go diff --git a/routers/repo/projects.go b/routers/web/repo/projects.go similarity index 100% rename from routers/repo/projects.go rename to routers/web/repo/projects.go diff --git a/routers/repo/projects_test.go b/routers/web/repo/projects_test.go similarity index 100% rename from routers/repo/projects_test.go rename to routers/web/repo/projects_test.go diff --git a/routers/repo/pull.go b/routers/web/repo/pull.go similarity index 100% rename from routers/repo/pull.go rename to routers/web/repo/pull.go diff --git a/routers/repo/pull_review.go b/routers/web/repo/pull_review.go similarity index 100% rename from routers/repo/pull_review.go rename to routers/web/repo/pull_review.go diff --git a/routers/repo/release.go b/routers/web/repo/release.go similarity index 100% rename from routers/repo/release.go rename to routers/web/repo/release.go diff --git a/routers/repo/release_test.go b/routers/web/repo/release_test.go similarity index 100% rename from routers/repo/release_test.go rename to routers/web/repo/release_test.go diff --git a/routers/repo/repo.go b/routers/web/repo/repo.go similarity index 95% rename from routers/repo/repo.go rename to routers/web/repo/repo.go index 69471a83d3..f149e92a8b 100644 --- a/routers/repo/repo.go +++ b/routers/web/repo/repo.go @@ -364,30 +364,6 @@ func RedirectDownload(ctx *context.Context) { ctx.Error(http.StatusNotFound) } -// Download an archive of a repository -func Download(ctx *context.Context) { - uri := ctx.Params("*") - aReq := archiver_service.DeriveRequestFrom(ctx, uri) - - if aReq == nil { - ctx.Error(http.StatusNotFound) - return - } - - downloadName := ctx.Repo.Repository.Name + "-" + aReq.GetArchiveName() - complete := aReq.IsComplete() - if !complete { - aReq = archiver_service.ArchiveRepository(aReq) - complete = aReq.WaitForCompletion(ctx) - } - - if complete { - ctx.ServeFile(aReq.GetArchivePath(), downloadName) - } else { - ctx.Error(http.StatusNotFound) - } -} - // InitiateDownload will enqueue an archival request, as needed. It may submit // a request that's already in-progress, but the archiver service will just // kind of drop it on the floor if this is the case. diff --git a/routers/repo/search.go b/routers/web/repo/search.go similarity index 100% rename from routers/repo/search.go rename to routers/web/repo/search.go diff --git a/routers/repo/setting.go b/routers/web/repo/setting.go similarity index 100% rename from routers/repo/setting.go rename to routers/web/repo/setting.go diff --git a/routers/repo/setting_protected_branch.go b/routers/web/repo/setting_protected_branch.go similarity index 100% rename from routers/repo/setting_protected_branch.go rename to routers/web/repo/setting_protected_branch.go diff --git a/routers/repo/settings_test.go b/routers/web/repo/settings_test.go similarity index 100% rename from routers/repo/settings_test.go rename to routers/web/repo/settings_test.go diff --git a/routers/repo/topic.go b/routers/web/repo/topic.go similarity index 100% rename from routers/repo/topic.go rename to routers/web/repo/topic.go diff --git a/routers/repo/view.go b/routers/web/repo/view.go similarity index 100% rename from routers/repo/view.go rename to routers/web/repo/view.go diff --git a/routers/repo/webhook.go b/routers/web/repo/webhook.go similarity index 100% rename from routers/repo/webhook.go rename to routers/web/repo/webhook.go diff --git a/routers/repo/wiki.go b/routers/web/repo/wiki.go similarity index 99% rename from routers/repo/wiki.go rename to routers/web/repo/wiki.go index 1bdd06dce5..cceb8451e5 100644 --- a/routers/repo/wiki.go +++ b/routers/web/repo/wiki.go @@ -24,6 +24,7 @@ import ( "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/services/forms" wiki_service "code.gitea.io/gitea/services/wiki" ) @@ -558,7 +559,7 @@ func WikiRaw(ctx *context.Context) { } if entry != nil { - if err = ServeBlob(ctx, entry.Blob()); err != nil { + if err = common.ServeBlob(ctx, entry.Blob()); err != nil { ctx.ServerError("ServeBlob", err) } return diff --git a/routers/repo/wiki_test.go b/routers/web/repo/wiki_test.go similarity index 100% rename from routers/repo/wiki_test.go rename to routers/web/repo/wiki_test.go diff --git a/routers/swagger_json.go b/routers/web/swagger_json.go similarity index 97% rename from routers/swagger_json.go rename to routers/web/swagger_json.go index 78c7fb1e24..82d72698c6 100644 --- a/routers/swagger_json.go +++ b/routers/web/swagger_json.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package routers +package web import ( "net/http" diff --git a/routers/user/auth.go b/routers/web/user/auth.go similarity index 100% rename from routers/user/auth.go rename to routers/web/user/auth.go diff --git a/routers/user/auth_openid.go b/routers/web/user/auth_openid.go similarity index 100% rename from routers/user/auth_openid.go rename to routers/web/user/auth_openid.go diff --git a/routers/user/avatar.go b/routers/web/user/avatar.go similarity index 100% rename from routers/user/avatar.go rename to routers/web/user/avatar.go diff --git a/routers/user/home.go b/routers/web/user/home.go similarity index 100% rename from routers/user/home.go rename to routers/web/user/home.go diff --git a/routers/user/home_test.go b/routers/web/user/home_test.go similarity index 100% rename from routers/user/home_test.go rename to routers/web/user/home_test.go diff --git a/routers/user/main_test.go b/routers/web/user/main_test.go similarity index 84% rename from routers/user/main_test.go rename to routers/web/user/main_test.go index ed0724dc77..be17dd1f31 100644 --- a/routers/user/main_test.go +++ b/routers/web/user/main_test.go @@ -12,5 +12,5 @@ import ( ) func TestMain(m *testing.M) { - models.MainTest(m, filepath.Join("..", "..")) + models.MainTest(m, filepath.Join("..", "..", "..")) } diff --git a/routers/user/notification.go b/routers/web/user/notification.go similarity index 100% rename from routers/user/notification.go rename to routers/web/user/notification.go diff --git a/routers/user/oauth.go b/routers/web/user/oauth.go similarity index 100% rename from routers/user/oauth.go rename to routers/web/user/oauth.go diff --git a/routers/user/profile.go b/routers/web/user/profile.go similarity index 99% rename from routers/user/profile.go rename to routers/web/user/profile.go index 8ff1ee24ad..e66820e131 100644 --- a/routers/user/profile.go +++ b/routers/web/user/profile.go @@ -17,7 +17,7 @@ import ( "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" - "code.gitea.io/gitea/routers/org" + "code.gitea.io/gitea/routers/web/org" ) // GetUserByName get user by name diff --git a/routers/user/setting/account.go b/routers/web/user/setting/account.go similarity index 100% rename from routers/user/setting/account.go rename to routers/web/user/setting/account.go diff --git a/routers/user/setting/account_test.go b/routers/web/user/setting/account_test.go similarity index 100% rename from routers/user/setting/account_test.go rename to routers/web/user/setting/account_test.go diff --git a/routers/user/setting/adopt.go b/routers/web/user/setting/adopt.go similarity index 100% rename from routers/user/setting/adopt.go rename to routers/web/user/setting/adopt.go diff --git a/routers/user/setting/applications.go b/routers/web/user/setting/applications.go similarity index 100% rename from routers/user/setting/applications.go rename to routers/web/user/setting/applications.go diff --git a/routers/user/setting/keys.go b/routers/web/user/setting/keys.go similarity index 100% rename from routers/user/setting/keys.go rename to routers/web/user/setting/keys.go diff --git a/routers/admin/main_test.go b/routers/web/user/setting/main_test.go similarity index 78% rename from routers/admin/main_test.go rename to routers/web/user/setting/main_test.go index 9a7191d471..daa3f7fe5b 100644 --- a/routers/admin/main_test.go +++ b/routers/web/user/setting/main_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package admin +package setting import ( "path/filepath" @@ -12,5 +12,5 @@ import ( ) func TestMain(m *testing.M) { - models.MainTest(m, filepath.Join("..", "..")) + models.MainTest(m, filepath.Join("..", "..", "..", "..")) } diff --git a/routers/user/setting/oauth2.go b/routers/web/user/setting/oauth2.go similarity index 100% rename from routers/user/setting/oauth2.go rename to routers/web/user/setting/oauth2.go diff --git a/routers/user/setting/profile.go b/routers/web/user/setting/profile.go similarity index 100% rename from routers/user/setting/profile.go rename to routers/web/user/setting/profile.go diff --git a/routers/user/setting/security.go b/routers/web/user/setting/security.go similarity index 100% rename from routers/user/setting/security.go rename to routers/web/user/setting/security.go diff --git a/routers/user/setting/security_openid.go b/routers/web/user/setting/security_openid.go similarity index 100% rename from routers/user/setting/security_openid.go rename to routers/web/user/setting/security_openid.go diff --git a/routers/user/setting/security_twofa.go b/routers/web/user/setting/security_twofa.go similarity index 100% rename from routers/user/setting/security_twofa.go rename to routers/web/user/setting/security_twofa.go diff --git a/routers/user/setting/security_u2f.go b/routers/web/user/setting/security_u2f.go similarity index 100% rename from routers/user/setting/security_u2f.go rename to routers/web/user/setting/security_u2f.go diff --git a/routers/user/task.go b/routers/web/user/task.go similarity index 100% rename from routers/user/task.go rename to routers/web/user/task.go diff --git a/routers/routes/web.go b/routers/web/web.go similarity index 92% rename from routers/routes/web.go rename to routers/web/web.go index fbc41d547d..6c0141eef3 100644 --- a/routers/routes/web.go +++ b/routers/web/web.go @@ -2,15 +2,13 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package routes +package web import ( "encoding/gob" - "fmt" "net/http" "os" "path" - "strings" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" @@ -23,17 +21,16 @@ import ( "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/routers/common" + "code.gitea.io/gitea/routers/web/admin" + "code.gitea.io/gitea/routers/web/dev" + "code.gitea.io/gitea/routers/web/events" + "code.gitea.io/gitea/routers/web/explore" + "code.gitea.io/gitea/routers/web/org" + "code.gitea.io/gitea/routers/web/repo" + "code.gitea.io/gitea/routers/web/user" + userSetting "code.gitea.io/gitea/routers/web/user/setting" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/lfs" "code.gitea.io/gitea/services/mailer" @@ -44,7 +41,6 @@ import ( "gitea.com/go-chi/captcha" "gitea.com/go-chi/session" "github.com/NYTimes/gziphandler" - "github.com/chi-middleware/proxy" "github.com/go-chi/chi/middleware" "github.com/go-chi/cors" "github.com/prometheus/client_golang/prometheus" @@ -56,74 +52,10 @@ const ( 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) - }) - }, - } - - if setting.ReverseProxyLimit > 0 { - opt := proxy.NewForwardedHeadersOptions(). - WithForwardLimit(setting.ReverseProxyLimit). - ClearTrustedProxies() - for _, n := range setting.ReverseProxyTrustedProxies { - if !strings.Contains(n, "/") { - opt.AddTrustedProxy(n) - } else { - opt.AddTrustedNetwork(n) - } - } - handlers = append(handlers, proxy.ForwardedHeaders(opt)) - } - - handlers = append(handlers, middleware.StripSlashes) - - if !setting.DisableRouterLog && setting.RouterLogLevel != log.NONE { - if log.GetLogger("router").GetLevel() <= setting.RouterLogLevel { - handlers = append(handlers, LoggerHandler(setting.RouterLogLevel)) - } - } - if setting.EnableAccessLog { - handlers = append(handlers, context.AccessLogger()) - } - - 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) - }) - }) - return handlers -} - -var corsHandler func(http.Handler) http.Handler - -// NormalRoutes represents non install routes -func NormalRoutes() *web.Route { - r := web.NewRoute() - for _, middle := range commonMiddlewares() { - r.Use(middle) - } - +// CorsHandler return a http handler who set CORS options if enabled by config +func CorsHandler() func(next http.Handler) http.Handler { if setting.CORSConfig.Enabled { - corsHandler = cors.Handler(cors.Options{ + return 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 @@ -131,26 +63,21 @@ func NormalRoutes() *web.Route { AllowCredentials: setting.CORSConfig.AllowCredentials, MaxAge: int(setting.CORSConfig.MaxAge.Seconds()), }) - } else { - corsHandler = func(next http.Handler) http.Handler { - return next - } } - r.Mount("/", WebRoutes()) - r.Mount("/api/v1", apiv1.Routes()) - r.Mount("/api/internal", private.Routes()) - return r + return func(next http.Handler) http.Handler { + return next + } } -// WebRoutes returns all web routes -func WebRoutes() *web.Route { +// Routes returns all web routes +func Routes() *web.Route { routes := web.NewRoute() routes.Use(public.AssetsHandler(&public.Options{ Directory: path.Join(setting.StaticRootPath, "public"), Prefix: "/assets", - CorsHandler: corsHandler, + CorsHandler: CorsHandler(), })) routes.Use(session.Sessioner(session.Options{ @@ -216,7 +143,7 @@ func WebRoutes() *web.Route { c := metrics.NewCollector() prometheus.MustRegister(c) - routes.Get("/metrics", append(common, routers.Metrics)...) + routes.Get("/metrics", append(common, Metrics)...) } // Removed: toolbox.Toolboxer middleware will provide debug informations which seems unnecessary @@ -297,16 +224,16 @@ func RegisterRoutes(m *web.Route) { // Especially some AJAX requests, we can reduce middleware number to improve performance. // Routers. // for health check - m.Get("/", routers.Home) + m.Get("/", Home) m.Get("/.well-known/openid-configuration", user.OIDCWellKnown) 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) + m.Get("/repos", explore.Repos) + m.Get("/users", explore.Users) + m.Get("/organizations", explore.Organizations) + m.Get("/code", explore.Code) }, ignExploreSignIn) m.Get("/issues", reqSignIn, user.Issues) m.Get("/pulls", reqSignIn, user.Pulls) @@ -363,7 +290,7 @@ func RegisterRoutes(m *web.Route) { m.Post("/authorize", bindIgnErr(forms.AuthorizationForm{}), user.AuthorizeOAuth) }, ignSignInAndCsrf, reqSignIn) m.Get("/login/oauth/userinfo", ignSignInAndCsrf, user.InfoOAuth) - m.Post("/login/oauth/access_token", corsHandler, bindIgnErr(forms.AccessTokenForm{}), ignSignInAndCsrf, user.AccessTokenOAuth) + m.Post("/login/oauth/access_token", CorsHandler(), bindIgnErr(forms.AccessTokenForm{}), ignSignInAndCsrf, user.AccessTokenOAuth) m.Group("/user/settings", func() { m.Get("", userSetting.Profile) @@ -956,7 +883,7 @@ func RegisterRoutes(m *web.Route) { }, context.RepoRef(), repo.MustBeNotEmpty, context.RequireRepoReaderOr(models.UnitTypeCode)) m.Group("/archive", func() { - m.Get("/*", repo.Download) + m.Get("/*", common.Download) m.Post("/*", repo.InitiateDownload) }, repo.MustBeNotEmpty, reqRepoCodeReader) @@ -1091,9 +1018,6 @@ func RegisterRoutes(m *web.Route) { }, reqSignIn) if setting.API.EnableSwagger { - m.Get("/swagger.v1.json", routers.SwaggerV1Json) + m.Get("/swagger.v1.json", SwaggerV1Json) } - - // Not found handler. - m.NotFound(web.Wrap(routers.NotFound)) }