mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-27 01:18:27 +00:00 
			
		
		
		
	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:
		| @@ -70,9 +70,6 @@ issues: | ||||
|     - path: modules/log/ | ||||
|       linters: | ||||
|         - errcheck | ||||
|     - path: routers/routes/web.go | ||||
|       linters: | ||||
|         - dupl | ||||
|     - path: routers/api/v1/repo/issue_subscription.go | ||||
|       linters: | ||||
|         - dupl | ||||
| @@ -114,3 +111,4 @@ issues: | ||||
|       linters: | ||||
|         - staticcheck | ||||
|       text: "svc.IsAnInteractiveSession is deprecated: Use IsWindowsService instead." | ||||
|  | ||||
|   | ||||
| @@ -17,7 +17,7 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/routers" | ||||
| 	"code.gitea.io/gitea/routers/routes" | ||||
| 	"code.gitea.io/gitea/routers/install" | ||||
|  | ||||
| 	context2 "github.com/gorilla/context" | ||||
| 	"github.com/urfave/cli" | ||||
| @@ -88,7 +88,7 @@ func runWeb(ctx *cli.Context) error { | ||||
| 	} | ||||
|  | ||||
| 	// Perform pre-initialization | ||||
| 	needsInstall := routers.PreInstallInit(graceful.GetManager().HammerContext()) | ||||
| 	needsInstall := install.PreloadSettings(graceful.GetManager().HammerContext()) | ||||
| 	if needsInstall { | ||||
| 		// Flag for port number in case first time run conflict | ||||
| 		if ctx.IsSet("port") { | ||||
| @@ -101,7 +101,7 @@ func runWeb(ctx *cli.Context) error { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 		c := routes.InstallRoutes() | ||||
| 		c := install.Routes() | ||||
| 		err := listen(c, false) | ||||
| 		select { | ||||
| 		case <-graceful.GetManager().IsShutdown(): | ||||
| @@ -134,7 +134,7 @@ func runWeb(ctx *cli.Context) error { | ||||
| 	} | ||||
|  | ||||
| 	// Set up Chi routes | ||||
| 	c := routes.NormalRoutes() | ||||
| 	c := routers.NormalRoutes() | ||||
| 	err := listen(c, true) | ||||
| 	<-graceful.GetManager().Done() | ||||
| 	log.Info("PID: %d Gitea Web Finished", os.Getpid()) | ||||
|   | ||||
| @@ -31,7 +31,6 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	"code.gitea.io/gitea/routers" | ||||
| 	"code.gitea.io/gitea/routers/routes" | ||||
|  | ||||
| 	"github.com/go-git/go-git/v5" | ||||
| 	"github.com/go-git/go-git/v5/config" | ||||
| @@ -116,7 +115,7 @@ func runPR() { | ||||
| 	//routers.GlobalInit() | ||||
| 	external.RegisterRenderers() | ||||
| 	markup.Init() | ||||
| 	c := routes.NormalRoutes() | ||||
| 	c := routers.NormalRoutes() | ||||
|  | ||||
| 	log.Printf("[PR] Ready for testing !\n") | ||||
| 	log.Printf("[PR] Login with user1, user2, user3, ... with pass: password\n") | ||||
|   | ||||
| @@ -14,7 +14,7 @@ import ( | ||||
|  | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	"code.gitea.io/gitea/routers/routes" | ||||
| 	"code.gitea.io/gitea/routers" | ||||
|  | ||||
| 	"gitea.com/go-chi/session" | ||||
| 	jsoniter "github.com/json-iterator/go" | ||||
| @@ -58,7 +58,7 @@ func TestSessionFileCreation(t *testing.T) { | ||||
| 	oldSessionConfig := setting.SessionConfig.ProviderConfig | ||||
| 	defer func() { | ||||
| 		setting.SessionConfig.ProviderConfig = oldSessionConfig | ||||
| 		c = routes.NormalRoutes() | ||||
| 		c = routers.NormalRoutes() | ||||
| 	}() | ||||
|  | ||||
| 	var config session.Options | ||||
| @@ -84,7 +84,7 @@ func TestSessionFileCreation(t *testing.T) { | ||||
|  | ||||
| 	setting.SessionConfig.ProviderConfig = string(newConfigBytes) | ||||
|  | ||||
| 	c = routes.NormalRoutes() | ||||
| 	c = routers.NormalRoutes() | ||||
|  | ||||
| 	t.Run("NoSessionOnViewIssue", func(t *testing.T) { | ||||
| 		defer PrintCurrentTest(t)() | ||||
|   | ||||
| @@ -34,7 +34,6 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	"code.gitea.io/gitea/modules/web" | ||||
| 	"code.gitea.io/gitea/routers" | ||||
| 	"code.gitea.io/gitea/routers/routes" | ||||
|  | ||||
| 	"github.com/PuerkitoBio/goquery" | ||||
| 	jsoniter "github.com/json-iterator/go" | ||||
| @@ -88,7 +87,7 @@ func TestMain(m *testing.M) { | ||||
| 	defer cancel() | ||||
|  | ||||
| 	initIntegrationTest() | ||||
| 	c = routes.NormalRoutes() | ||||
| 	c = routers.NormalRoutes() | ||||
|  | ||||
| 	// integration test settings... | ||||
| 	if setting.Cfg != nil { | ||||
|   | ||||
| @@ -15,7 +15,7 @@ import ( | ||||
| 	"code.gitea.io/gitea/models" | ||||
| 	"code.gitea.io/gitea/modules/lfs" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/routers/routes" | ||||
| 	"code.gitea.io/gitea/routers/web" | ||||
|  | ||||
| 	jsoniter "github.com/json-iterator/go" | ||||
| 	gzipp "github.com/klauspost/compress/gzip" | ||||
| @@ -99,7 +99,7 @@ func TestGetLFSLarge(t *testing.T) { | ||||
| 		t.Skip() | ||||
| 		return | ||||
| 	} | ||||
| 	content := make([]byte, routes.GzipMinSize*10) | ||||
| 	content := make([]byte, web.GzipMinSize*10) | ||||
| 	for i := range content { | ||||
| 		content[i] = byte(i % 256) | ||||
| 	} | ||||
| @@ -115,7 +115,7 @@ func TestGetLFSGzip(t *testing.T) { | ||||
| 		t.Skip() | ||||
| 		return | ||||
| 	} | ||||
| 	b := make([]byte, routes.GzipMinSize*10) | ||||
| 	b := make([]byte, web.GzipMinSize*10) | ||||
| 	for i := range b { | ||||
| 		b[i] = byte(i % 256) | ||||
| 	} | ||||
| @@ -136,7 +136,7 @@ func TestGetLFSZip(t *testing.T) { | ||||
| 		t.Skip() | ||||
| 		return | ||||
| 	} | ||||
| 	b := make([]byte, routes.GzipMinSize*10) | ||||
| 	b := make([]byte, web.GzipMinSize*10) | ||||
| 	for i := range b { | ||||
| 		b[i] = byte(i % 256) | ||||
| 	} | ||||
|   | ||||
| @@ -17,7 +17,7 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/repofiles" | ||||
| 	api "code.gitea.io/gitea/modules/structs" | ||||
| 	"code.gitea.io/gitea/modules/web" | ||||
| 	"code.gitea.io/gitea/routers/repo" | ||||
| 	"code.gitea.io/gitea/routers/common" | ||||
| ) | ||||
|  | ||||
| // GetRawFile get a file by path on a repository | ||||
| @@ -83,7 +83,7 @@ func GetRawFile(ctx *context.APIContext) { | ||||
| 		} | ||||
| 		return | ||||
| 	} | ||||
| 	if err = repo.ServeBlob(ctx.Context, blob); err != nil { | ||||
| 	if err = common.ServeBlob(ctx.Context, blob); err != nil { | ||||
| 		ctx.Error(http.StatusInternalServerError, "ServeBlob", err) | ||||
| 	} | ||||
| } | ||||
| @@ -126,7 +126,7 @@ func GetArchive(ctx *context.APIContext) { | ||||
| 	ctx.Repo.GitRepo = gitRepo | ||||
| 	defer gitRepo.Close() | ||||
|  | ||||
| 	repo.Download(ctx.Context) | ||||
| 	common.Download(ctx.Context) | ||||
| } | ||||
|  | ||||
| // GetEditorconfig get editor config of a repository | ||||
|   | ||||
							
								
								
									
										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 ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"code.gitea.io/gitea/models" | ||||
| 	"code.gitea.io/gitea/models/migrations" | ||||
| 	"code.gitea.io/gitea/modules/auth/sso" | ||||
| 	"code.gitea.io/gitea/modules/cache" | ||||
| 	"code.gitea.io/gitea/modules/cron" | ||||
| @@ -32,6 +29,11 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/svg" | ||||
| 	"code.gitea.io/gitea/modules/task" | ||||
| 	"code.gitea.io/gitea/modules/translation" | ||||
| 	"code.gitea.io/gitea/modules/web" | ||||
| 	apiv1 "code.gitea.io/gitea/routers/api/v1" | ||||
| 	"code.gitea.io/gitea/routers/common" | ||||
| 	"code.gitea.io/gitea/routers/private" | ||||
| 	web_routers "code.gitea.io/gitea/routers/web" | ||||
| 	"code.gitea.io/gitea/services/mailer" | ||||
| 	mirror_service "code.gitea.io/gitea/services/mirror" | ||||
| 	pull_service "code.gitea.io/gitea/services/pull" | ||||
| @@ -63,63 +65,6 @@ func NewServices() { | ||||
| 	notification.NewContext() | ||||
| } | ||||
|  | ||||
| // In case of problems connecting to DB, retry connection. Eg, PGSQL in Docker Container on Synology | ||||
| func initDBEngine(ctx context.Context) (err error) { | ||||
| 	log.Info("Beginning ORM engine initialization.") | ||||
| 	for i := 0; i < setting.Database.DBConnectRetries; i++ { | ||||
| 		select { | ||||
| 		case <-ctx.Done(): | ||||
| 			return fmt.Errorf("Aborted due to shutdown:\nin retry ORM engine initialization") | ||||
| 		default: | ||||
| 		} | ||||
| 		log.Info("ORM engine initialization attempt #%d/%d...", i+1, setting.Database.DBConnectRetries) | ||||
| 		if err = models.NewEngine(ctx, migrations.Migrate); err == nil { | ||||
| 			break | ||||
| 		} else if i == setting.Database.DBConnectRetries-1 { | ||||
| 			return err | ||||
| 		} | ||||
| 		log.Error("ORM engine initialization attempt #%d/%d failed. Error: %v", i+1, setting.Database.DBConnectRetries, err) | ||||
| 		log.Info("Backing off for %d seconds", int64(setting.Database.DBConnectBackoff/time.Second)) | ||||
| 		time.Sleep(setting.Database.DBConnectBackoff) | ||||
| 	} | ||||
| 	models.HasEngine = true | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // PreInstallInit preloads the configuration to check if we need to run install | ||||
| func PreInstallInit(ctx context.Context) bool { | ||||
| 	setting.NewContext() | ||||
| 	if !setting.InstallLock { | ||||
| 		log.Trace("AppPath: %s", setting.AppPath) | ||||
| 		log.Trace("AppWorkPath: %s", setting.AppWorkPath) | ||||
| 		log.Trace("Custom path: %s", setting.CustomPath) | ||||
| 		log.Trace("Log path: %s", setting.LogRootPath) | ||||
| 		log.Trace("Preparing to run install page") | ||||
| 		translation.InitLocales() | ||||
| 		if setting.EnableSQLite3 { | ||||
| 			log.Info("SQLite3 Supported") | ||||
| 		} | ||||
| 		setting.InitDBConfig() | ||||
| 		svg.Init() | ||||
| 	} | ||||
|  | ||||
| 	return !setting.InstallLock | ||||
| } | ||||
|  | ||||
| // PostInstallInit rereads the settings and starts up the database | ||||
| func PostInstallInit(ctx context.Context) { | ||||
| 	setting.NewContext() | ||||
| 	setting.InitDBConfig() | ||||
| 	if setting.InstallLock { | ||||
| 		if err := initDBEngine(ctx); err == nil { | ||||
| 			log.Info("ORM engine initialization successful!") | ||||
| 		} else { | ||||
| 			log.Fatal("ORM engine initialization failed: %v", err) | ||||
| 		} | ||||
| 		svg.Init() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // GlobalInit is for global configuration reload-able. | ||||
| func GlobalInit(ctx context.Context) { | ||||
| 	setting.NewContext() | ||||
| @@ -151,7 +96,7 @@ func GlobalInit(ctx context.Context) { | ||||
| 	} else if setting.Database.UseSQLite3 { | ||||
| 		log.Fatal("SQLite3 is set in settings but NOT Supported") | ||||
| 	} | ||||
| 	if err := initDBEngine(ctx); err == nil { | ||||
| 	if err := common.InitDBEngine(ctx); err == nil { | ||||
| 		log.Info("ORM engine initialization successful!") | ||||
| 	} else { | ||||
| 		log.Fatal("ORM engine initialization failed: %v", err) | ||||
| @@ -193,3 +138,16 @@ func GlobalInit(ctx context.Context) { | ||||
|  | ||||
| 	svg.Init() | ||||
| } | ||||
|  | ||||
| // NormalRoutes represents non install routes | ||||
| func NormalRoutes() *web.Route { | ||||
| 	r := web.NewRoute() | ||||
| 	for _, middle := range common.Middlewares() { | ||||
| 		r.Use(middle) | ||||
| 	} | ||||
|  | ||||
| 	r.Mount("/", web_routers.Routes()) | ||||
| 	r.Mount("/api/v1", apiv1.Routes()) | ||||
| 	r.Mount("/api/internal", private.Routes()) | ||||
| 	return r | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,9 @@ | ||||
| // Copyright 2014 The Gogs Authors. All rights reserved. | ||||
| // Copyright 2021 The Gitea Authors. All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package routers | ||||
| package install | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| @@ -38,8 +39,8 @@ const ( | ||||
| 	tplPostInstall base.TplName = "post-install" | ||||
| ) | ||||
| 
 | ||||
| // InstallInit prepare for rendering installation page | ||||
| func InstallInit(next http.Handler) http.Handler { | ||||
| // Init prepare for rendering installation page | ||||
| func Init(next http.Handler) http.Handler { | ||||
| 	var rnd = templates.HTMLRenderer() | ||||
| 
 | ||||
| 	return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { | ||||
| @@ -158,8 +159,8 @@ func Install(ctx *context.Context) { | ||||
| 	ctx.HTML(http.StatusOK, tplInstall) | ||||
| } | ||||
| 
 | ||||
| // InstallPost response for submit install items | ||||
| func InstallPost(ctx *context.Context) { | ||||
| // SubmitInstall response for submit install items | ||||
| func SubmitInstall(ctx *context.Context) { | ||||
| 	form := *web.GetForm(ctx).(*forms.InstallForm) | ||||
| 	var err error | ||||
| 	ctx.Data["CurDbOption"] = form.DbType | ||||
| @@ -409,7 +410,7 @@ func InstallPost(ctx *context.Context) { | ||||
| 	} | ||||
| 
 | ||||
| 	// Re-read settings | ||||
| 	PostInstallInit(ctx) | ||||
| 	ReloadSettings(ctx) | ||||
| 
 | ||||
| 	// Create admin account | ||||
| 	if len(form.AdminName) > 0 { | ||||
| @@ -2,7 +2,7 @@ | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package routes | ||||
| package install | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| @@ -15,12 +15,18 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/templates" | ||||
| 	"code.gitea.io/gitea/modules/web" | ||||
| 	"code.gitea.io/gitea/modules/web/middleware" | ||||
| 	"code.gitea.io/gitea/routers" | ||||
| 	"code.gitea.io/gitea/routers/common" | ||||
| 	"code.gitea.io/gitea/services/forms" | ||||
| 
 | ||||
| 	"gitea.com/go-chi/session" | ||||
| ) | ||||
| 
 | ||||
| type dataStore map[string]interface{} | ||||
| 
 | ||||
| func (d *dataStore) GetData() map[string]interface{} { | ||||
| 	return *d | ||||
| } | ||||
| 
 | ||||
| func installRecovery() func(next http.Handler) http.Handler { | ||||
| 	var rnd = templates.HTMLRenderer() | ||||
| 	return func(next http.Handler) http.Handler { | ||||
| @@ -48,21 +54,19 @@ func installRecovery() func(next http.Handler) http.Handler { | ||||
| 
 | ||||
| 					lc := middleware.Locale(w, req) | ||||
| 					var store = dataStore{ | ||||
| 						Data: templates.Vars{ | ||||
| 							"Language":       lc.Language(), | ||||
| 							"CurrentURL":     setting.AppSubURL + req.URL.RequestURI(), | ||||
| 							"i18n":           lc, | ||||
| 							"SignedUserID":   int64(0), | ||||
| 							"SignedUserName": "", | ||||
| 						}, | ||||
| 						"Language":       lc.Language(), | ||||
| 						"CurrentURL":     setting.AppSubURL + req.URL.RequestURI(), | ||||
| 						"i18n":           lc, | ||||
| 						"SignedUserID":   int64(0), | ||||
| 						"SignedUserName": "", | ||||
| 					} | ||||
| 
 | ||||
| 					w.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) | ||||
| 
 | ||||
| 					if !setting.IsProd() { | ||||
| 						store.Data["ErrorMsg"] = combinedErr | ||||
| 						store["ErrorMsg"] = combinedErr | ||||
| 					} | ||||
| 					err = rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store.Data)) | ||||
| 					err = rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store)) | ||||
| 					if err != nil { | ||||
| 						log.Error("%v", err) | ||||
| 					} | ||||
| @@ -74,10 +78,10 @@ func installRecovery() func(next http.Handler) http.Handler { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // InstallRoutes registers the install routes | ||||
| func InstallRoutes() *web.Route { | ||||
| // Routes registers the install routes | ||||
| func Routes() *web.Route { | ||||
| 	r := web.NewRoute() | ||||
| 	for _, middle := range commonMiddlewares() { | ||||
| 	for _, middle := range common.Middlewares() { | ||||
| 		r.Use(middle) | ||||
| 	} | ||||
| 
 | ||||
| @@ -99,9 +103,9 @@ func InstallRoutes() *web.Route { | ||||
| 	})) | ||||
| 
 | ||||
| 	r.Use(installRecovery()) | ||||
| 	r.Use(routers.InstallInit) | ||||
| 	r.Get("/", routers.Install) | ||||
| 	r.Post("/", web.Bind(forms.InstallForm{}), routers.InstallPost) | ||||
| 	r.Use(Init) | ||||
| 	r.Get("/", Install) | ||||
| 	r.Post("/", web.Bind(forms.InstallForm{}), SubmitInstall) | ||||
| 	r.NotFound(func(w http.ResponseWriter, req *http.Request) { | ||||
| 		http.Redirect(w, req, setting.AppURL, http.StatusFound) | ||||
| 	}) | ||||
							
								
								
									
										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 | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package setting | ||||
| package admin | ||||
| 
 | ||||
| import ( | ||||
| 	"path/filepath" | ||||
| @@ -11,7 +11,7 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/context" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/structs" | ||||
| 	"code.gitea.io/gitea/routers" | ||||
| 	"code.gitea.io/gitea/routers/web/explore" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| @@ -24,7 +24,7 @@ func Organizations(ctx *context.Context) { | ||||
| 	ctx.Data["PageIsAdmin"] = true | ||||
| 	ctx.Data["PageIsAdminOrganizations"] = true | ||||
| 
 | ||||
| 	routers.RenderUserSearch(ctx, &models.SearchUserOptions{ | ||||
| 	explore.RenderUserSearch(ctx, &models.SearchUserOptions{ | ||||
| 		Type: models.UserTypeOrganization, | ||||
| 		ListOptions: models.ListOptions{ | ||||
| 			PageSize: setting.UI.Admin.OrgPagingNum, | ||||
| @@ -17,7 +17,7 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/repository" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	"code.gitea.io/gitea/routers" | ||||
| 	"code.gitea.io/gitea/routers/web/explore" | ||||
| 	repo_service "code.gitea.io/gitea/services/repository" | ||||
| ) | ||||
| 
 | ||||
| @@ -32,7 +32,7 @@ func Repos(ctx *context.Context) { | ||||
| 	ctx.Data["PageIsAdmin"] = true | ||||
| 	ctx.Data["PageIsAdminRepositories"] = true | ||||
| 
 | ||||
| 	routers.RenderRepoSearch(ctx, &routers.RepoSearchOptions{ | ||||
| 	explore.RenderRepoSearch(ctx, &explore.RepoSearchOptions{ | ||||
| 		Private:  true, | ||||
| 		PageSize: setting.UI.Admin.RepoPagingNum, | ||||
| 		TplName:  tplRepos, | ||||
| @@ -18,8 +18,8 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/password" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/web" | ||||
| 	"code.gitea.io/gitea/routers" | ||||
| 	router_user_setting "code.gitea.io/gitea/routers/user/setting" | ||||
| 	"code.gitea.io/gitea/routers/web/explore" | ||||
| 	router_user_setting "code.gitea.io/gitea/routers/web/user/setting" | ||||
| 	"code.gitea.io/gitea/services/forms" | ||||
| 	"code.gitea.io/gitea/services/mailer" | ||||
| ) | ||||
| @@ -36,7 +36,7 @@ func Users(ctx *context.Context) { | ||||
| 	ctx.Data["PageIsAdmin"] = true | ||||
| 	ctx.Data["PageIsAdminUsers"] = true | ||||
| 
 | ||||
| 	routers.RenderUserSearch(ctx, &models.SearchUserOptions{ | ||||
| 	explore.RenderUserSearch(ctx, &models.SearchUserOptions{ | ||||
| 		Type: models.UserTypeIndividual, | ||||
| 		ListOptions: models.ListOptions{ | ||||
| 			PageSize: setting.UI.Admin.UserPagingNum, | ||||
| @@ -2,7 +2,7 @@ | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package routes | ||||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| @@ -13,7 +13,6 @@ import ( | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models" | ||||
| 	"code.gitea.io/gitea/modules/auth/sso" | ||||
| @@ -28,26 +27,6 @@ import ( | ||||
| 	"gitea.com/go-chi/session" | ||||
| ) | ||||
| 
 | ||||
| // LoggerHandler is a handler that will log the routing to the default gitea log | ||||
| func LoggerHandler(level log.Level) func(next http.Handler) http.Handler { | ||||
| 	return func(next http.Handler) http.Handler { | ||||
| 		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||||
| 			start := time.Now() | ||||
| 
 | ||||
| 			_ = log.GetLogger("router").Log(0, level, "Started %s %s for %s", log.ColoredMethod(req.Method), req.URL.RequestURI(), req.RemoteAddr) | ||||
| 
 | ||||
| 			next.ServeHTTP(w, req) | ||||
| 
 | ||||
| 			var status int | ||||
| 			if v, ok := w.(context.ResponseWriter); ok { | ||||
| 				status = v.Status() | ||||
| 			} | ||||
| 
 | ||||
| 			_ = log.GetLogger("router").Log(0, level, "Completed %s %s %v %s in %v", log.ColoredMethod(req.Method), req.URL.RequestURI(), log.ColoredStatus(status), log.ColoredStatus(status, http.StatusText(status)), log.ColoredTime(time.Since(start))) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func storageHandler(storageSetting setting.Storage, prefix string, objStore storage.ObjectStorage) func(next http.Handler) http.Handler { | ||||
| 	return func(next http.Handler) http.Handler { | ||||
| 		if storageSetting.ServeDirect { | ||||
| @@ -134,12 +113,10 @@ func storageHandler(storageSetting setting.Storage, prefix string, objStore stor | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| type dataStore struct { | ||||
| 	Data map[string]interface{} | ||||
| } | ||||
| type dataStore map[string]interface{} | ||||
| 
 | ||||
| func (d *dataStore) GetData() map[string]interface{} { | ||||
| 	return d.Data | ||||
| 	return *d | ||||
| } | ||||
| 
 | ||||
| // Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so. | ||||
| @@ -165,11 +142,9 @@ func Recovery() func(next http.Handler) http.Handler { | ||||
| 
 | ||||
| 					var lc = middleware.Locale(w, req) | ||||
| 					var store = dataStore{ | ||||
| 						Data: templates.Vars{ | ||||
| 							"Language":   lc.Language(), | ||||
| 							"CurrentURL": setting.AppSubURL + req.URL.RequestURI(), | ||||
| 							"i18n":       lc, | ||||
| 						}, | ||||
| 						"Language":   lc.Language(), | ||||
| 						"CurrentURL": setting.AppSubURL + req.URL.RequestURI(), | ||||
| 						"i18n":       lc, | ||||
| 					} | ||||
| 
 | ||||
| 					var user *models.User | ||||
| @@ -186,22 +161,22 @@ func Recovery() func(next http.Handler) http.Handler { | ||||
| 						user = sso.SessionUser(sessionStore) | ||||
| 					} | ||||
| 					if user != nil { | ||||
| 						store.Data["IsSigned"] = true | ||||
| 						store.Data["SignedUser"] = user | ||||
| 						store.Data["SignedUserID"] = user.ID | ||||
| 						store.Data["SignedUserName"] = user.Name | ||||
| 						store.Data["IsAdmin"] = user.IsAdmin | ||||
| 						store["IsSigned"] = true | ||||
| 						store["SignedUser"] = user | ||||
| 						store["SignedUserID"] = user.ID | ||||
| 						store["SignedUserName"] = user.Name | ||||
| 						store["IsAdmin"] = user.IsAdmin | ||||
| 					} else { | ||||
| 						store.Data["SignedUserID"] = int64(0) | ||||
| 						store.Data["SignedUserName"] = "" | ||||
| 						store["SignedUserID"] = int64(0) | ||||
| 						store["SignedUserName"] = "" | ||||
| 					} | ||||
| 
 | ||||
| 					w.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) | ||||
| 
 | ||||
| 					if !setting.IsProd() { | ||||
| 						store.Data["ErrorMsg"] = combinedErr | ||||
| 						store["ErrorMsg"] = combinedErr | ||||
| 					} | ||||
| 					err = rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store.Data)) | ||||
| 					err = rnd.HTML(w, 500, "status/500", templates.BaseVars().Merge(store)) | ||||
| 					if err != nil { | ||||
| 						log.Error("%v", err) | ||||
| 					} | ||||
| @@ -15,7 +15,7 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/graceful" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/routers/user" | ||||
| 	"code.gitea.io/gitea/routers/web/user" | ||||
| 	jsoniter "github.com/json-iterator/go" | ||||
| ) | ||||
| 
 | ||||
							
								
								
									
										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 | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package routes | ||||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"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 | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package routers | ||||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/subtle" | ||||
| @@ -15,7 +15,7 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/web" | ||||
| 	userSetting "code.gitea.io/gitea/routers/user/setting" | ||||
| 	userSetting "code.gitea.io/gitea/routers/web/user/setting" | ||||
| 	"code.gitea.io/gitea/services/forms" | ||||
| ) | ||||
| 
 | ||||
| @@ -15,6 +15,7 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/storage" | ||||
| 	"code.gitea.io/gitea/modules/upload" | ||||
| 	"code.gitea.io/gitea/routers/common" | ||||
| ) | ||||
| 
 | ||||
| // UploadIssueAttachment response for Issue/PR attachments | ||||
| @@ -152,7 +153,7 @@ func GetAttachment(ctx *context.Context) { | ||||
| 	} | ||||
| 	defer fr.Close() | ||||
| 
 | ||||
| 	if err = ServeData(ctx, attach.Name, attach.Size, fr); err != nil { | ||||
| 	if err = common.ServeData(ctx, attach.Name, attach.Size, fr); err != nil { | ||||
| 		ctx.ServerError("ServeData", err) | ||||
| 		return | ||||
| 	} | ||||
| @@ -6,102 +6,14 @@ | ||||
| package repo | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"path" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/modules/charset" | ||||
| 	"code.gitea.io/gitea/modules/context" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	"code.gitea.io/gitea/modules/httpcache" | ||||
| 	"code.gitea.io/gitea/modules/lfs" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/typesniffer" | ||||
| 	"code.gitea.io/gitea/routers/common" | ||||
| ) | ||||
| 
 | ||||
| // ServeData download file from io.Reader | ||||
| func ServeData(ctx *context.Context, name string, size int64, reader io.Reader) error { | ||||
| 	buf := make([]byte, 1024) | ||||
| 	n, err := reader.Read(buf) | ||||
| 	if err != nil && err != io.EOF { | ||||
| 		return err | ||||
| 	} | ||||
| 	if n >= 0 { | ||||
| 		buf = buf[:n] | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.Resp.Header().Set("Cache-Control", "public,max-age=86400") | ||||
| 
 | ||||
| 	if size >= 0 { | ||||
| 		ctx.Resp.Header().Set("Content-Length", fmt.Sprintf("%d", size)) | ||||
| 	} else { | ||||
| 		log.Error("ServeData called to serve data: %s with size < 0: %d", name, size) | ||||
| 	} | ||||
| 	name = path.Base(name) | ||||
| 
 | ||||
| 	// Google Chrome dislike commas in filenames, so let's change it to a space | ||||
| 	name = strings.ReplaceAll(name, ",", " ") | ||||
| 
 | ||||
| 	st := typesniffer.DetectContentType(buf) | ||||
| 
 | ||||
| 	if st.IsText() || ctx.QueryBool("render") { | ||||
| 		cs, err := charset.DetectEncoding(buf) | ||||
| 		if err != nil { | ||||
| 			log.Error("Detect raw file %s charset failed: %v, using by default utf-8", name, err) | ||||
| 			cs = "utf-8" | ||||
| 		} | ||||
| 		ctx.Resp.Header().Set("Content-Type", "text/plain; charset="+strings.ToLower(cs)) | ||||
| 	} else { | ||||
| 		ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition") | ||||
| 
 | ||||
| 		if (st.IsImage() || st.IsPDF()) && (setting.UI.SVG.Enabled || !st.IsSvgImage()) { | ||||
| 			ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, name)) | ||||
| 			if st.IsSvgImage() { | ||||
| 				ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox") | ||||
| 				ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff") | ||||
| 				ctx.Resp.Header().Set("Content-Type", typesniffer.SvgMimeType) | ||||
| 			} | ||||
| 		} else { | ||||
| 			ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name)) | ||||
| 			if setting.MimeTypeMap.Enabled { | ||||
| 				fileExtension := strings.ToLower(filepath.Ext(name)) | ||||
| 				if mimetype, ok := setting.MimeTypeMap.Map[fileExtension]; ok { | ||||
| 					ctx.Resp.Header().Set("Content-Type", mimetype) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = ctx.Resp.Write(buf) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	_, err = io.Copy(ctx.Resp, reader) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // ServeBlob download a git.Blob | ||||
| func ServeBlob(ctx *context.Context, blob *git.Blob) error { | ||||
| 	if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`) { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	dataRc, err := blob.DataAsync() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		if err = dataRc.Close(); err != nil { | ||||
| 			log.Error("ServeBlob: Close: %v", err) | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	return ServeData(ctx, ctx.Repo.TreePath, blob.Size(), dataRc) | ||||
| } | ||||
| 
 | ||||
| // ServeBlobOrLFS download a git.Blob redirecting to LFS if necessary | ||||
| func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob) error { | ||||
| 	if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`) { | ||||
| @@ -130,7 +42,7 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob) error { | ||||
| 				log.Error("ServeBlobOrLFS: Close: %v", err) | ||||
| 			} | ||||
| 			closed = true | ||||
| 			return ServeBlob(ctx, blob) | ||||
| 			return common.ServeBlob(ctx, blob) | ||||
| 		} | ||||
| 		if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+pointer.Oid+`"`) { | ||||
| 			return nil | ||||
| @@ -144,14 +56,14 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob) error { | ||||
| 				log.Error("ServeBlobOrLFS: Close: %v", err) | ||||
| 			} | ||||
| 		}() | ||||
| 		return ServeData(ctx, ctx.Repo.TreePath, meta.Size, lfsDataRc) | ||||
| 		return common.ServeData(ctx, ctx.Repo.TreePath, meta.Size, lfsDataRc) | ||||
| 	} | ||||
| 	if err = dataRc.Close(); err != nil { | ||||
| 		log.Error("ServeBlobOrLFS: Close: %v", err) | ||||
| 	} | ||||
| 	closed = true | ||||
| 
 | ||||
| 	return ServeBlob(ctx, blob) | ||||
| 	return common.ServeBlob(ctx, blob) | ||||
| } | ||||
| 
 | ||||
| // SingleDownload download a file by repos path | ||||
| @@ -165,7 +77,7 @@ func SingleDownload(ctx *context.Context) { | ||||
| 		} | ||||
| 		return | ||||
| 	} | ||||
| 	if err = ServeBlob(ctx, blob); err != nil { | ||||
| 	if err = common.ServeBlob(ctx, blob); err != nil { | ||||
| 		ctx.ServerError("ServeBlob", err) | ||||
| 	} | ||||
| } | ||||
| @@ -197,7 +109,7 @@ func DownloadByID(ctx *context.Context) { | ||||
| 		} | ||||
| 		return | ||||
| 	} | ||||
| 	if err = ServeBlob(ctx, blob); err != nil { | ||||
| 	if err = common.ServeBlob(ctx, blob); err != nil { | ||||
| 		ctx.ServerError("ServeBlob", err) | ||||
| 	} | ||||
| } | ||||
| @@ -12,5 +12,5 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| 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) | ||||
| } | ||||
| 
 | ||||
| // Download an archive of a repository | ||||
| func Download(ctx *context.Context) { | ||||
| 	uri := ctx.Params("*") | ||||
| 	aReq := archiver_service.DeriveRequestFrom(ctx, uri) | ||||
| 
 | ||||
| 	if aReq == nil { | ||||
| 		ctx.Error(http.StatusNotFound) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	downloadName := ctx.Repo.Repository.Name + "-" + aReq.GetArchiveName() | ||||
| 	complete := aReq.IsComplete() | ||||
| 	if !complete { | ||||
| 		aReq = archiver_service.ArchiveRepository(aReq) | ||||
| 		complete = aReq.WaitForCompletion(ctx) | ||||
| 	} | ||||
| 
 | ||||
| 	if complete { | ||||
| 		ctx.ServeFile(aReq.GetArchivePath(), downloadName) | ||||
| 	} else { | ||||
| 		ctx.Error(http.StatusNotFound) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // InitiateDownload will enqueue an archival request, as needed.  It may submit | ||||
| // a request that's already in-progress, but the archiver service will just | ||||
| // kind of drop it on the floor if this is the case. | ||||
| @@ -24,6 +24,7 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/timeutil" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	"code.gitea.io/gitea/modules/web" | ||||
| 	"code.gitea.io/gitea/routers/common" | ||||
| 	"code.gitea.io/gitea/services/forms" | ||||
| 	wiki_service "code.gitea.io/gitea/services/wiki" | ||||
| ) | ||||
| @@ -558,7 +559,7 @@ func WikiRaw(ctx *context.Context) { | ||||
| 	} | ||||
| 
 | ||||
| 	if entry != nil { | ||||
| 		if err = ServeBlob(ctx, entry.Blob()); err != nil { | ||||
| 		if err = common.ServeBlob(ctx, entry.Blob()); err != nil { | ||||
| 			ctx.ServerError("ServeBlob", err) | ||||
| 		} | ||||
| 		return | ||||
| @@ -2,7 +2,7 @@ | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package routers | ||||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| @@ -12,5 +12,5 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| 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/setting" | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| 	"code.gitea.io/gitea/routers/org" | ||||
| 	"code.gitea.io/gitea/routers/web/org" | ||||
| ) | ||||
| 
 | ||||
| // GetUserByName get user by name | ||||
| @@ -2,7 +2,7 @@ | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
| 
 | ||||
| package admin | ||||
| package setting | ||||
| 
 | ||||
| import ( | ||||
| 	"path/filepath" | ||||
| @@ -12,5 +12,5 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| func TestMain(m *testing.M) { | ||||
| 	models.MainTest(m, filepath.Join("..", "..")) | ||||
| 	models.MainTest(m, filepath.Join("..", "..", "..", "..")) | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user