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>release/v1.15
parent
e03a91a48e
commit
1bfb0a24d8
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
})
|
})
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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,
|
||||||
|
})
|
||||||
|
}
|
|
@ -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"
|
|
@ -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 New Issue