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>
This commit is contained in:
		
							parent
							
								
									e03a91a48e
								
							
						
					
					
						commit
						1bfb0a24d8
					
				
					 107 changed files with 940 additions and 800 deletions
				
			
		|  | @ -70,9 +70,6 @@ issues: | ||||||
|     - path: modules/log/ |     - path: modules/log/ | ||||||
|       linters: |       linters: | ||||||
|         - errcheck |         - errcheck | ||||||
|     - path: routers/routes/web.go |  | ||||||
|       linters: |  | ||||||
|         - dupl |  | ||||||
|     - path: routers/api/v1/repo/issue_subscription.go |     - path: routers/api/v1/repo/issue_subscription.go | ||||||
|       linters: |       linters: | ||||||
|         - dupl |         - dupl | ||||||
|  | @ -114,3 +111,4 @@ issues: | ||||||
|       linters: |       linters: | ||||||
|         - staticcheck |         - staticcheck | ||||||
|       text: "svc.IsAnInteractiveSession is deprecated: Use IsWindowsService instead." |       text: "svc.IsAnInteractiveSession is deprecated: Use IsWindowsService instead." | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -17,7 +17,7 @@ import ( | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/routers" | 	"code.gitea.io/gitea/routers" | ||||||
| 	"code.gitea.io/gitea/routers/routes" | 	"code.gitea.io/gitea/routers/install" | ||||||
| 
 | 
 | ||||||
| 	context2 "github.com/gorilla/context" | 	context2 "github.com/gorilla/context" | ||||||
| 	"github.com/urfave/cli" | 	"github.com/urfave/cli" | ||||||
|  | @ -88,7 +88,7 @@ func runWeb(ctx *cli.Context) error { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Perform pre-initialization
 | 	// Perform pre-initialization
 | ||||||
| 	needsInstall := routers.PreInstallInit(graceful.GetManager().HammerContext()) | 	needsInstall := install.PreloadSettings(graceful.GetManager().HammerContext()) | ||||||
| 	if needsInstall { | 	if needsInstall { | ||||||
| 		// Flag for port number in case first time run conflict
 | 		// Flag for port number in case first time run conflict
 | ||||||
| 		if ctx.IsSet("port") { | 		if ctx.IsSet("port") { | ||||||
|  | @ -101,7 +101,7 @@ func runWeb(ctx *cli.Context) error { | ||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		c := routes.InstallRoutes() | 		c := install.Routes() | ||||||
| 		err := listen(c, false) | 		err := listen(c, false) | ||||||
| 		select { | 		select { | ||||||
| 		case <-graceful.GetManager().IsShutdown(): | 		case <-graceful.GetManager().IsShutdown(): | ||||||
|  | @ -134,7 +134,7 @@ func runWeb(ctx *cli.Context) error { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Set up Chi routes
 | 	// Set up Chi routes
 | ||||||
| 	c := routes.NormalRoutes() | 	c := routers.NormalRoutes() | ||||||
| 	err := listen(c, true) | 	err := listen(c, true) | ||||||
| 	<-graceful.GetManager().Done() | 	<-graceful.GetManager().Done() | ||||||
| 	log.Info("PID: %d Gitea Web Finished", os.Getpid()) | 	log.Info("PID: %d Gitea Web Finished", os.Getpid()) | ||||||
|  |  | ||||||
|  | @ -31,7 +31,6 @@ import ( | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/util" | 	"code.gitea.io/gitea/modules/util" | ||||||
| 	"code.gitea.io/gitea/routers" | 	"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" | ||||||
| 	"github.com/go-git/go-git/v5/config" | 	"github.com/go-git/go-git/v5/config" | ||||||
|  | @ -116,7 +115,7 @@ func runPR() { | ||||||
| 	//routers.GlobalInit()
 | 	//routers.GlobalInit()
 | ||||||
| 	external.RegisterRenderers() | 	external.RegisterRenderers() | ||||||
| 	markup.Init() | 	markup.Init() | ||||||
| 	c := routes.NormalRoutes() | 	c := routers.NormalRoutes() | ||||||
| 
 | 
 | ||||||
| 	log.Printf("[PR] Ready for testing !\n") | 	log.Printf("[PR] Ready for testing !\n") | ||||||
| 	log.Printf("[PR] Login with user1, user2, user3, ... with pass: password\n") | 	log.Printf("[PR] Login with user1, user2, user3, ... with pass: password\n") | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ import ( | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/util" | 	"code.gitea.io/gitea/modules/util" | ||||||
| 	"code.gitea.io/gitea/routers/routes" | 	"code.gitea.io/gitea/routers" | ||||||
| 
 | 
 | ||||||
| 	"gitea.com/go-chi/session" | 	"gitea.com/go-chi/session" | ||||||
| 	jsoniter "github.com/json-iterator/go" | 	jsoniter "github.com/json-iterator/go" | ||||||
|  | @ -58,7 +58,7 @@ func TestSessionFileCreation(t *testing.T) { | ||||||
| 	oldSessionConfig := setting.SessionConfig.ProviderConfig | 	oldSessionConfig := setting.SessionConfig.ProviderConfig | ||||||
| 	defer func() { | 	defer func() { | ||||||
| 		setting.SessionConfig.ProviderConfig = oldSessionConfig | 		setting.SessionConfig.ProviderConfig = oldSessionConfig | ||||||
| 		c = routes.NormalRoutes() | 		c = routers.NormalRoutes() | ||||||
| 	}() | 	}() | ||||||
| 
 | 
 | ||||||
| 	var config session.Options | 	var config session.Options | ||||||
|  | @ -84,7 +84,7 @@ func TestSessionFileCreation(t *testing.T) { | ||||||
| 
 | 
 | ||||||
| 	setting.SessionConfig.ProviderConfig = string(newConfigBytes) | 	setting.SessionConfig.ProviderConfig = string(newConfigBytes) | ||||||
| 
 | 
 | ||||||
| 	c = routes.NormalRoutes() | 	c = routers.NormalRoutes() | ||||||
| 
 | 
 | ||||||
| 	t.Run("NoSessionOnViewIssue", func(t *testing.T) { | 	t.Run("NoSessionOnViewIssue", func(t *testing.T) { | ||||||
| 		defer PrintCurrentTest(t)() | 		defer PrintCurrentTest(t)() | ||||||
|  |  | ||||||
|  | @ -34,7 +34,6 @@ import ( | ||||||
| 	"code.gitea.io/gitea/modules/util" | 	"code.gitea.io/gitea/modules/util" | ||||||
| 	"code.gitea.io/gitea/modules/web" | 	"code.gitea.io/gitea/modules/web" | ||||||
| 	"code.gitea.io/gitea/routers" | 	"code.gitea.io/gitea/routers" | ||||||
| 	"code.gitea.io/gitea/routers/routes" |  | ||||||
| 
 | 
 | ||||||
| 	"github.com/PuerkitoBio/goquery" | 	"github.com/PuerkitoBio/goquery" | ||||||
| 	jsoniter "github.com/json-iterator/go" | 	jsoniter "github.com/json-iterator/go" | ||||||
|  | @ -88,7 +87,7 @@ func TestMain(m *testing.M) { | ||||||
| 	defer cancel() | 	defer cancel() | ||||||
| 
 | 
 | ||||||
| 	initIntegrationTest() | 	initIntegrationTest() | ||||||
| 	c = routes.NormalRoutes() | 	c = routers.NormalRoutes() | ||||||
| 
 | 
 | ||||||
| 	// integration test settings...
 | 	// integration test settings...
 | ||||||
| 	if setting.Cfg != nil { | 	if setting.Cfg != nil { | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ import ( | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/modules/lfs" | 	"code.gitea.io/gitea/modules/lfs" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/routers/routes" | 	"code.gitea.io/gitea/routers/web" | ||||||
| 
 | 
 | ||||||
| 	jsoniter "github.com/json-iterator/go" | 	jsoniter "github.com/json-iterator/go" | ||||||
| 	gzipp "github.com/klauspost/compress/gzip" | 	gzipp "github.com/klauspost/compress/gzip" | ||||||
|  | @ -99,7 +99,7 @@ func TestGetLFSLarge(t *testing.T) { | ||||||
| 		t.Skip() | 		t.Skip() | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	content := make([]byte, routes.GzipMinSize*10) | 	content := make([]byte, web.GzipMinSize*10) | ||||||
| 	for i := range content { | 	for i := range content { | ||||||
| 		content[i] = byte(i % 256) | 		content[i] = byte(i % 256) | ||||||
| 	} | 	} | ||||||
|  | @ -115,7 +115,7 @@ func TestGetLFSGzip(t *testing.T) { | ||||||
| 		t.Skip() | 		t.Skip() | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	b := make([]byte, routes.GzipMinSize*10) | 	b := make([]byte, web.GzipMinSize*10) | ||||||
| 	for i := range b { | 	for i := range b { | ||||||
| 		b[i] = byte(i % 256) | 		b[i] = byte(i % 256) | ||||||
| 	} | 	} | ||||||
|  | @ -136,7 +136,7 @@ func TestGetLFSZip(t *testing.T) { | ||||||
| 		t.Skip() | 		t.Skip() | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	b := make([]byte, routes.GzipMinSize*10) | 	b := make([]byte, web.GzipMinSize*10) | ||||||
| 	for i := range b { | 	for i := range b { | ||||||
| 		b[i] = byte(i % 256) | 		b[i] = byte(i % 256) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -17,7 +17,7 @@ import ( | ||||||
| 	"code.gitea.io/gitea/modules/repofiles" | 	"code.gitea.io/gitea/modules/repofiles" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| 	"code.gitea.io/gitea/modules/web" | 	"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
 | // GetRawFile get a file by path on a repository
 | ||||||
|  | @ -83,7 +83,7 @@ func GetRawFile(ctx *context.APIContext) { | ||||||
| 		} | 		} | ||||||
| 		return | 		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) | 		ctx.Error(http.StatusInternalServerError, "ServeBlob", err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -126,7 +126,7 @@ func GetArchive(ctx *context.APIContext) { | ||||||
| 	ctx.Repo.GitRepo = gitRepo | 	ctx.Repo.GitRepo = gitRepo | ||||||
| 	defer gitRepo.Close() | 	defer gitRepo.Close() | ||||||
| 
 | 
 | ||||||
| 	repo.Download(ctx.Context) | 	common.Download(ctx.Context) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetEditorconfig get editor config of a repository
 | // GetEditorconfig get editor config of a repository
 | ||||||
|  |  | ||||||
							
								
								
									
										39
									
								
								routers/common/db.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								routers/common/db.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||||
|  | } | ||||||
							
								
								
									
										33
									
								
								routers/common/logger.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								routers/common/logger.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -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))) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										76
									
								
								routers/common/middleware.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								routers/common/middleware.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||||
|  | } | ||||||
							
								
								
									
										127
									
								
								routers/common/repo.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								routers/common/repo.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||||
|  | } | ||||||
							
								
								
									
										413
									
								
								routers/home.go
									
									
									
									
									
								
							
							
						
						
									
										413
									
								
								routers/home.go
									
									
									
									
									
								
							|  | @ -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) |  | ||||||
| } |  | ||||||
|  | @ -6,12 +6,9 @@ package routers | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" |  | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" |  | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/migrations" |  | ||||||
| 	"code.gitea.io/gitea/modules/auth/sso" | 	"code.gitea.io/gitea/modules/auth/sso" | ||||||
| 	"code.gitea.io/gitea/modules/cache" | 	"code.gitea.io/gitea/modules/cache" | ||||||
| 	"code.gitea.io/gitea/modules/cron" | 	"code.gitea.io/gitea/modules/cron" | ||||||
|  | @ -32,6 +29,11 @@ import ( | ||||||
| 	"code.gitea.io/gitea/modules/svg" | 	"code.gitea.io/gitea/modules/svg" | ||||||
| 	"code.gitea.io/gitea/modules/task" | 	"code.gitea.io/gitea/modules/task" | ||||||
| 	"code.gitea.io/gitea/modules/translation" | 	"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" | 	"code.gitea.io/gitea/services/mailer" | ||||||
| 	mirror_service "code.gitea.io/gitea/services/mirror" | 	mirror_service "code.gitea.io/gitea/services/mirror" | ||||||
| 	pull_service "code.gitea.io/gitea/services/pull" | 	pull_service "code.gitea.io/gitea/services/pull" | ||||||
|  | @ -63,63 +65,6 @@ func NewServices() { | ||||||
| 	notification.NewContext() | 	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.
 | // GlobalInit is for global configuration reload-able.
 | ||||||
| func GlobalInit(ctx context.Context) { | func GlobalInit(ctx context.Context) { | ||||||
| 	setting.NewContext() | 	setting.NewContext() | ||||||
|  | @ -151,7 +96,7 @@ func GlobalInit(ctx context.Context) { | ||||||
| 	} else if setting.Database.UseSQLite3 { | 	} else if setting.Database.UseSQLite3 { | ||||||
| 		log.Fatal("SQLite3 is set in settings but NOT Supported") | 		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!") | 		log.Info("ORM engine initialization successful!") | ||||||
| 	} else { | 	} else { | ||||||
| 		log.Fatal("ORM engine initialization failed: %v", err) | 		log.Fatal("ORM engine initialization failed: %v", err) | ||||||
|  | @ -193,3 +138,16 @@ func GlobalInit(ctx context.Context) { | ||||||
| 
 | 
 | ||||||
| 	svg.Init() | 	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 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1,8 +1,9 @@ | ||||||
| // Copyright 2014 The Gogs Authors. All rights reserved.
 | // 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
 | // Use of this source code is governed by a MIT-style
 | ||||||
| // license that can be found in the LICENSE file.
 | // license that can be found in the LICENSE file.
 | ||||||
| 
 | 
 | ||||||
| package routers | package install | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | @ -38,8 +39,8 @@ const ( | ||||||
| 	tplPostInstall base.TplName = "post-install" | 	tplPostInstall base.TplName = "post-install" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // InstallInit prepare for rendering installation page
 | // Init prepare for rendering installation page
 | ||||||
| func InstallInit(next http.Handler) http.Handler { | func Init(next http.Handler) http.Handler { | ||||||
| 	var rnd = templates.HTMLRenderer() | 	var rnd = templates.HTMLRenderer() | ||||||
| 
 | 
 | ||||||
| 	return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { | 	return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { | ||||||
|  | @ -158,8 +159,8 @@ func Install(ctx *context.Context) { | ||||||
| 	ctx.HTML(http.StatusOK, tplInstall) | 	ctx.HTML(http.StatusOK, tplInstall) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // InstallPost response for submit install items
 | // SubmitInstall response for submit install items
 | ||||||
| func InstallPost(ctx *context.Context) { | func SubmitInstall(ctx *context.Context) { | ||||||
| 	form := *web.GetForm(ctx).(*forms.InstallForm) | 	form := *web.GetForm(ctx).(*forms.InstallForm) | ||||||
| 	var err error | 	var err error | ||||||
| 	ctx.Data["CurDbOption"] = form.DbType | 	ctx.Data["CurDbOption"] = form.DbType | ||||||
|  | @ -409,7 +410,7 @@ func InstallPost(ctx *context.Context) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Re-read settings
 | 	// Re-read settings
 | ||||||
| 	PostInstallInit(ctx) | 	ReloadSettings(ctx) | ||||||
| 
 | 
 | ||||||
| 	// Create admin account
 | 	// Create admin account
 | ||||||
| 	if len(form.AdminName) > 0 { | 	if len(form.AdminName) > 0 { | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| // Use of this source code is governed by a MIT-style
 | // Use of this source code is governed by a MIT-style
 | ||||||
| // license that can be found in the LICENSE file.
 | // license that can be found in the LICENSE file.
 | ||||||
| 
 | 
 | ||||||
| package routes | package install | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | @ -15,12 +15,18 @@ import ( | ||||||
| 	"code.gitea.io/gitea/modules/templates" | 	"code.gitea.io/gitea/modules/templates" | ||||||
| 	"code.gitea.io/gitea/modules/web" | 	"code.gitea.io/gitea/modules/web" | ||||||
| 	"code.gitea.io/gitea/modules/web/middleware" | 	"code.gitea.io/gitea/modules/web/middleware" | ||||||
| 	"code.gitea.io/gitea/routers" | 	"code.gitea.io/gitea/routers/common" | ||||||
| 	"code.gitea.io/gitea/services/forms" | 	"code.gitea.io/gitea/services/forms" | ||||||
| 
 | 
 | ||||||
| 	"gitea.com/go-chi/session" | 	"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 { | func installRecovery() func(next http.Handler) http.Handler { | ||||||
| 	var rnd = templates.HTMLRenderer() | 	var rnd = templates.HTMLRenderer() | ||||||
| 	return func(next http.Handler) http.Handler { | 	return func(next http.Handler) http.Handler { | ||||||
|  | @ -48,21 +54,19 @@ func installRecovery() func(next http.Handler) http.Handler { | ||||||
| 
 | 
 | ||||||
| 					lc := middleware.Locale(w, req) | 					lc := middleware.Locale(w, req) | ||||||
| 					var store = dataStore{ | 					var store = dataStore{ | ||||||
| 						Data: templates.Vars{ | 						"Language":       lc.Language(), | ||||||
| 							"Language":       lc.Language(), | 						"CurrentURL":     setting.AppSubURL + req.URL.RequestURI(), | ||||||
| 							"CurrentURL":     setting.AppSubURL + req.URL.RequestURI(), | 						"i18n":           lc, | ||||||
| 							"i18n":           lc, | 						"SignedUserID":   int64(0), | ||||||
| 							"SignedUserID":   int64(0), | 						"SignedUserName": "", | ||||||
| 							"SignedUserName": "", |  | ||||||
| 						}, |  | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
| 					w.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) | 					w.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) | ||||||
| 
 | 
 | ||||||
| 					if !setting.IsProd() { | 					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 { | 					if err != nil { | ||||||
| 						log.Error("%v", err) | 						log.Error("%v", err) | ||||||
| 					} | 					} | ||||||
|  | @ -74,10 +78,10 @@ func installRecovery() func(next http.Handler) http.Handler { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // InstallRoutes registers the install routes
 | // Routes registers the install routes
 | ||||||
| func InstallRoutes() *web.Route { | func Routes() *web.Route { | ||||||
| 	r := web.NewRoute() | 	r := web.NewRoute() | ||||||
| 	for _, middle := range commonMiddlewares() { | 	for _, middle := range common.Middlewares() { | ||||||
| 		r.Use(middle) | 		r.Use(middle) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -99,9 +103,9 @@ func InstallRoutes() *web.Route { | ||||||
| 	})) | 	})) | ||||||
| 
 | 
 | ||||||
| 	r.Use(installRecovery()) | 	r.Use(installRecovery()) | ||||||
| 	r.Use(routers.InstallInit) | 	r.Use(Init) | ||||||
| 	r.Get("/", routers.Install) | 	r.Get("/", Install) | ||||||
| 	r.Post("/", web.Bind(forms.InstallForm{}), routers.InstallPost) | 	r.Post("/", web.Bind(forms.InstallForm{}), SubmitInstall) | ||||||
| 	r.NotFound(func(w http.ResponseWriter, req *http.Request) { | 	r.NotFound(func(w http.ResponseWriter, req *http.Request) { | ||||||
| 		http.Redirect(w, req, setting.AppURL, http.StatusFound) | 		http.Redirect(w, req, setting.AppURL, http.StatusFound) | ||||||
| 	}) | 	}) | ||||||
							
								
								
									
										49
									
								
								routers/install/setting.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								routers/install/setting.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -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() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| // Use of this source code is governed by a MIT-style
 | // Use of this source code is governed by a MIT-style
 | ||||||
| // license that can be found in the LICENSE file.
 | // license that can be found in the LICENSE file.
 | ||||||
| 
 | 
 | ||||||
| package setting | package admin | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
|  | @ -11,7 +11,7 @@ import ( | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/structs" | 	"code.gitea.io/gitea/modules/structs" | ||||||
| 	"code.gitea.io/gitea/routers" | 	"code.gitea.io/gitea/routers/web/explore" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
|  | @ -24,7 +24,7 @@ func Organizations(ctx *context.Context) { | ||||||
| 	ctx.Data["PageIsAdmin"] = true | 	ctx.Data["PageIsAdmin"] = true | ||||||
| 	ctx.Data["PageIsAdminOrganizations"] = true | 	ctx.Data["PageIsAdminOrganizations"] = true | ||||||
| 
 | 
 | ||||||
| 	routers.RenderUserSearch(ctx, &models.SearchUserOptions{ | 	explore.RenderUserSearch(ctx, &models.SearchUserOptions{ | ||||||
| 		Type: models.UserTypeOrganization, | 		Type: models.UserTypeOrganization, | ||||||
| 		ListOptions: models.ListOptions{ | 		ListOptions: models.ListOptions{ | ||||||
| 			PageSize: setting.UI.Admin.OrgPagingNum, | 			PageSize: setting.UI.Admin.OrgPagingNum, | ||||||
|  | @ -17,7 +17,7 @@ import ( | ||||||
| 	"code.gitea.io/gitea/modules/repository" | 	"code.gitea.io/gitea/modules/repository" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/util" | 	"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" | 	repo_service "code.gitea.io/gitea/services/repository" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -32,7 +32,7 @@ func Repos(ctx *context.Context) { | ||||||
| 	ctx.Data["PageIsAdmin"] = true | 	ctx.Data["PageIsAdmin"] = true | ||||||
| 	ctx.Data["PageIsAdminRepositories"] = true | 	ctx.Data["PageIsAdminRepositories"] = true | ||||||
| 
 | 
 | ||||||
| 	routers.RenderRepoSearch(ctx, &routers.RepoSearchOptions{ | 	explore.RenderRepoSearch(ctx, &explore.RepoSearchOptions{ | ||||||
| 		Private:  true, | 		Private:  true, | ||||||
| 		PageSize: setting.UI.Admin.RepoPagingNum, | 		PageSize: setting.UI.Admin.RepoPagingNum, | ||||||
| 		TplName:  tplRepos, | 		TplName:  tplRepos, | ||||||
|  | @ -18,8 +18,8 @@ import ( | ||||||
| 	"code.gitea.io/gitea/modules/password" | 	"code.gitea.io/gitea/modules/password" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/web" | 	"code.gitea.io/gitea/modules/web" | ||||||
| 	"code.gitea.io/gitea/routers" | 	"code.gitea.io/gitea/routers/web/explore" | ||||||
| 	router_user_setting "code.gitea.io/gitea/routers/user/setting" | 	router_user_setting "code.gitea.io/gitea/routers/web/user/setting" | ||||||
| 	"code.gitea.io/gitea/services/forms" | 	"code.gitea.io/gitea/services/forms" | ||||||
| 	"code.gitea.io/gitea/services/mailer" | 	"code.gitea.io/gitea/services/mailer" | ||||||
| ) | ) | ||||||
|  | @ -36,7 +36,7 @@ func Users(ctx *context.Context) { | ||||||
| 	ctx.Data["PageIsAdmin"] = true | 	ctx.Data["PageIsAdmin"] = true | ||||||
| 	ctx.Data["PageIsAdminUsers"] = true | 	ctx.Data["PageIsAdminUsers"] = true | ||||||
| 
 | 
 | ||||||
| 	routers.RenderUserSearch(ctx, &models.SearchUserOptions{ | 	explore.RenderUserSearch(ctx, &models.SearchUserOptions{ | ||||||
| 		Type: models.UserTypeIndividual, | 		Type: models.UserTypeIndividual, | ||||||
| 		ListOptions: models.ListOptions{ | 		ListOptions: models.ListOptions{ | ||||||
| 			PageSize: setting.UI.Admin.UserPagingNum, | 			PageSize: setting.UI.Admin.UserPagingNum, | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| // Use of this source code is governed by a MIT-style
 | // Use of this source code is governed by a MIT-style
 | ||||||
| // license that can be found in the LICENSE file.
 | // license that can be found in the LICENSE file.
 | ||||||
| 
 | 
 | ||||||
| package routes | package web | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
|  | @ -13,7 +13,6 @@ import ( | ||||||
| 	"path" | 	"path" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" |  | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/modules/auth/sso" | 	"code.gitea.io/gitea/modules/auth/sso" | ||||||
|  | @ -28,26 +27,6 @@ import ( | ||||||
| 	"gitea.com/go-chi/session" | 	"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 { | func storageHandler(storageSetting setting.Storage, prefix string, objStore storage.ObjectStorage) func(next http.Handler) http.Handler { | ||||||
| 	return func(next http.Handler) http.Handler { | 	return func(next http.Handler) http.Handler { | ||||||
| 		if storageSetting.ServeDirect { | 		if storageSetting.ServeDirect { | ||||||
|  | @ -134,12 +113,10 @@ func storageHandler(storageSetting setting.Storage, prefix string, objStore stor | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type dataStore struct { | type dataStore map[string]interface{} | ||||||
| 	Data map[string]interface{} |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| func (d *dataStore) GetData() 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.
 | // 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 lc = middleware.Locale(w, req) | ||||||
| 					var store = dataStore{ | 					var store = dataStore{ | ||||||
| 						Data: templates.Vars{ | 						"Language":   lc.Language(), | ||||||
| 							"Language":   lc.Language(), | 						"CurrentURL": setting.AppSubURL + req.URL.RequestURI(), | ||||||
| 							"CurrentURL": setting.AppSubURL + req.URL.RequestURI(), | 						"i18n":       lc, | ||||||
| 							"i18n":       lc, |  | ||||||
| 						}, |  | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
| 					var user *models.User | 					var user *models.User | ||||||
|  | @ -186,22 +161,22 @@ func Recovery() func(next http.Handler) http.Handler { | ||||||
| 						user = sso.SessionUser(sessionStore) | 						user = sso.SessionUser(sessionStore) | ||||||
| 					} | 					} | ||||||
| 					if user != nil { | 					if user != nil { | ||||||
| 						store.Data["IsSigned"] = true | 						store["IsSigned"] = true | ||||||
| 						store.Data["SignedUser"] = user | 						store["SignedUser"] = user | ||||||
| 						store.Data["SignedUserID"] = user.ID | 						store["SignedUserID"] = user.ID | ||||||
| 						store.Data["SignedUserName"] = user.Name | 						store["SignedUserName"] = user.Name | ||||||
| 						store.Data["IsAdmin"] = user.IsAdmin | 						store["IsAdmin"] = user.IsAdmin | ||||||
| 					} else { | 					} else { | ||||||
| 						store.Data["SignedUserID"] = int64(0) | 						store["SignedUserID"] = int64(0) | ||||||
| 						store.Data["SignedUserName"] = "" | 						store["SignedUserName"] = "" | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
| 					w.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) | 					w.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) | ||||||
| 
 | 
 | ||||||
| 					if !setting.IsProd() { | 					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 { | 					if err != nil { | ||||||
| 						log.Error("%v", err) | 						log.Error("%v", err) | ||||||
| 					} | 					} | ||||||
|  | @ -15,7 +15,7 @@ import ( | ||||||
| 	"code.gitea.io/gitea/modules/graceful" | 	"code.gitea.io/gitea/modules/graceful" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"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" | 	jsoniter "github.com/json-iterator/go" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
							
								
								
									
										139
									
								
								routers/web/explore/code.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								routers/web/explore/code.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -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) | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								routers/web/explore/org.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								routers/web/explore/org.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -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) | ||||||
|  | } | ||||||
							
								
								
									
										131
									
								
								routers/web/explore/repo.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								routers/web/explore/repo.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -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, | ||||||
|  | 	}) | ||||||
|  | } | ||||||
							
								
								
									
										107
									
								
								routers/web/explore/user.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								routers/web/explore/user.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -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) | ||||||
|  | } | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| // Use of this source code is governed by a MIT-style
 | // Use of this source code is governed by a MIT-style
 | ||||||
| // license that can be found in the LICENSE file.
 | // license that can be found in the LICENSE file.
 | ||||||
| 
 | 
 | ||||||
| package routes | package web | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"net/http" | 	"net/http" | ||||||
							
								
								
									
										65
									
								
								routers/web/home.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								routers/web/home.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -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) | ||||||
|  | } | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| // Use of this source code is governed by a MIT-style
 | // Use of this source code is governed by a MIT-style
 | ||||||
| // license that can be found in the LICENSE file.
 | // license that can be found in the LICENSE file.
 | ||||||
| 
 | 
 | ||||||
| package routers | package web | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"crypto/subtle" | 	"crypto/subtle" | ||||||
|  | @ -15,7 +15,7 @@ import ( | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/web" | 	"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" | 	"code.gitea.io/gitea/services/forms" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -15,6 +15,7 @@ import ( | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/storage" | 	"code.gitea.io/gitea/modules/storage" | ||||||
| 	"code.gitea.io/gitea/modules/upload" | 	"code.gitea.io/gitea/modules/upload" | ||||||
|  | 	"code.gitea.io/gitea/routers/common" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // UploadIssueAttachment response for Issue/PR attachments
 | // UploadIssueAttachment response for Issue/PR attachments
 | ||||||
|  | @ -152,7 +153,7 @@ func GetAttachment(ctx *context.Context) { | ||||||
| 	} | 	} | ||||||
| 	defer fr.Close() | 	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) | 		ctx.ServerError("ServeData", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  | @ -6,102 +6,14 @@ | ||||||
| package repo | package repo | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" |  | ||||||
| 	"io" |  | ||||||
| 	"path" |  | ||||||
| 	"path/filepath" |  | ||||||
| 	"strings" |  | ||||||
| 
 |  | ||||||
| 	"code.gitea.io/gitea/modules/charset" |  | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| 	"code.gitea.io/gitea/modules/git" | 	"code.gitea.io/gitea/modules/git" | ||||||
| 	"code.gitea.io/gitea/modules/httpcache" | 	"code.gitea.io/gitea/modules/httpcache" | ||||||
| 	"code.gitea.io/gitea/modules/lfs" | 	"code.gitea.io/gitea/modules/lfs" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/routers/common" | ||||||
| 	"code.gitea.io/gitea/modules/typesniffer" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // 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
 | // ServeBlobOrLFS download a git.Blob redirecting to LFS if necessary
 | ||||||
| func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob) error { | func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob) error { | ||||||
| 	if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`) { | 	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) | 				log.Error("ServeBlobOrLFS: Close: %v", err) | ||||||
| 			} | 			} | ||||||
| 			closed = true | 			closed = true | ||||||
| 			return ServeBlob(ctx, blob) | 			return common.ServeBlob(ctx, blob) | ||||||
| 		} | 		} | ||||||
| 		if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+pointer.Oid+`"`) { | 		if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+pointer.Oid+`"`) { | ||||||
| 			return nil | 			return nil | ||||||
|  | @ -144,14 +56,14 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob) error { | ||||||
| 				log.Error("ServeBlobOrLFS: Close: %v", err) | 				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 { | 	if err = dataRc.Close(); err != nil { | ||||||
| 		log.Error("ServeBlobOrLFS: Close: %v", err) | 		log.Error("ServeBlobOrLFS: Close: %v", err) | ||||||
| 	} | 	} | ||||||
| 	closed = true | 	closed = true | ||||||
| 
 | 
 | ||||||
| 	return ServeBlob(ctx, blob) | 	return common.ServeBlob(ctx, blob) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SingleDownload download a file by repos path
 | // SingleDownload download a file by repos path
 | ||||||
|  | @ -165,7 +77,7 @@ func SingleDownload(ctx *context.Context) { | ||||||
| 		} | 		} | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	if err = ServeBlob(ctx, blob); err != nil { | 	if err = common.ServeBlob(ctx, blob); err != nil { | ||||||
| 		ctx.ServerError("ServeBlob", err) | 		ctx.ServerError("ServeBlob", err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -197,7 +109,7 @@ func DownloadByID(ctx *context.Context) { | ||||||
| 		} | 		} | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	if err = ServeBlob(ctx, blob); err != nil { | 	if err = common.ServeBlob(ctx, blob); err != nil { | ||||||
| 		ctx.ServerError("ServeBlob", err) | 		ctx.ServerError("ServeBlob", err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -12,5 +12,5 @@ import ( | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestMain(m *testing.M) { | func TestMain(m *testing.M) { | ||||||
| 	models.MainTest(m, filepath.Join("..", "..")) | 	models.MainTest(m, filepath.Join("..", "..", "..")) | ||||||
| } | } | ||||||
|  | @ -364,30 +364,6 @@ func RedirectDownload(ctx *context.Context) { | ||||||
| 	ctx.Error(http.StatusNotFound) | 	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
 | // InitiateDownload will enqueue an archival request, as needed.  It may submit
 | ||||||
| // a request that's already in-progress, but the archiver service will just
 | // 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.
 | // kind of drop it on the floor if this is the case.
 | ||||||
|  | @ -24,6 +24,7 @@ import ( | ||||||
| 	"code.gitea.io/gitea/modules/timeutil" | 	"code.gitea.io/gitea/modules/timeutil" | ||||||
| 	"code.gitea.io/gitea/modules/util" | 	"code.gitea.io/gitea/modules/util" | ||||||
| 	"code.gitea.io/gitea/modules/web" | 	"code.gitea.io/gitea/modules/web" | ||||||
|  | 	"code.gitea.io/gitea/routers/common" | ||||||
| 	"code.gitea.io/gitea/services/forms" | 	"code.gitea.io/gitea/services/forms" | ||||||
| 	wiki_service "code.gitea.io/gitea/services/wiki" | 	wiki_service "code.gitea.io/gitea/services/wiki" | ||||||
| ) | ) | ||||||
|  | @ -558,7 +559,7 @@ func WikiRaw(ctx *context.Context) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if entry != nil { | 	if entry != nil { | ||||||
| 		if err = ServeBlob(ctx, entry.Blob()); err != nil { | 		if err = common.ServeBlob(ctx, entry.Blob()); err != nil { | ||||||
| 			ctx.ServerError("ServeBlob", err) | 			ctx.ServerError("ServeBlob", err) | ||||||
| 		} | 		} | ||||||
| 		return | 		return | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| // Use of this source code is governed by a MIT-style
 | // Use of this source code is governed by a MIT-style
 | ||||||
| // license that can be found in the LICENSE file.
 | // license that can be found in the LICENSE file.
 | ||||||
| 
 | 
 | ||||||
| package routers | package web | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | @ -12,5 +12,5 @@ import ( | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestMain(m *testing.M) { | func TestMain(m *testing.M) { | ||||||
| 	models.MainTest(m, filepath.Join("..", "..")) | 	models.MainTest(m, filepath.Join("..", "..", "..")) | ||||||
| } | } | ||||||
|  | @ -17,7 +17,7 @@ import ( | ||||||
| 	"code.gitea.io/gitea/modules/markup/markdown" | 	"code.gitea.io/gitea/modules/markup/markdown" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/util" | 	"code.gitea.io/gitea/modules/util" | ||||||
| 	"code.gitea.io/gitea/routers/org" | 	"code.gitea.io/gitea/routers/web/org" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // GetUserByName get user by name
 | // GetUserByName get user by name
 | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| // Use of this source code is governed by a MIT-style
 | // Use of this source code is governed by a MIT-style
 | ||||||
| // license that can be found in the LICENSE file.
 | // license that can be found in the LICENSE file.
 | ||||||
| 
 | 
 | ||||||
| package admin | package setting | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
|  | @ -12,5 +12,5 @@ import ( | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestMain(m *testing.M) { | func TestMain(m *testing.M) { | ||||||
| 	models.MainTest(m, filepath.Join("..", "..")) | 	models.MainTest(m, filepath.Join("..", "..", "..", "..")) | ||||||
| } | } | ||||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
		Reference in a new issue