Skip to content

Commit

Permalink
Merge branch 'main' into support-trace
Browse files Browse the repository at this point in the history
  • Loading branch information
wxiaoguang authored Jan 20, 2025
2 parents 5647c41 + 6073e2f commit 4ebb00a
Show file tree
Hide file tree
Showing 13 changed files with 208 additions and 75 deletions.
2 changes: 1 addition & 1 deletion modules/git/repo_branch_gogit.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (repo *Repository) IsBranchExist(name string) bool {

// GetBranches returns branches from the repository, skipping "skip" initial branches and
// returning at most "limit" branches, or all branches if "limit" is 0.
// Branches are returned with sort of `-commiterdate` as the nogogit
// Branches are returned with sort of `-committerdate` as the nogogit
// implementation. This requires full fetch, sort and then the
// skip/limit applies later as gogit returns in undefined order.
func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) {
Expand Down
6 changes: 6 additions & 0 deletions options/gitignore/Node
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ dist
.temp
.cache

# vitepress build output
**/.vitepress/dist

# vitepress cache directory
**/.vitepress/cache

# Docusaurus cache and generated files
.docusaurus

Expand Down
3 changes: 3 additions & 0 deletions options/gitignore/Python
Original file line number Diff line number Diff line change
Expand Up @@ -167,5 +167,8 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# Ruff stuff:
.ruff_cache/

# PyPI configuration file
.pypirc
4 changes: 0 additions & 4 deletions options/gitignore/Rust
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
debug/
target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

Expand Down
10 changes: 7 additions & 3 deletions routers/common/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ func ProtocolMiddlewares() (handlers []any) {

func RequestContextHandler() func(h http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
return http.HandlerFunc(func(respOrig http.ResponseWriter, req *http.Request) {
// this response writer might not be the same as the one in context.Base.Resp
// because there might be a "gzip writer" in the middle, so the "written size" here is the compressed size
respWriter := context.WrapResponseWriter(respOrig)

profDesc := fmt.Sprintf("HTTP: %s %s", req.Method, req.RequestURI)
ctx, finished := reqctx.NewRequestContext(req.Context(), profDesc)
defer finished()
Expand All @@ -59,7 +63,7 @@ func RequestContextHandler() func(h http.Handler) http.Handler {

defer func() {
if err := recover(); err != nil {
RenderPanicErrorPage(resp, req, err) // it should never panic
RenderPanicErrorPage(respWriter, req, err) // it should never panic
}
}()

Expand All @@ -71,7 +75,7 @@ func RequestContextHandler() func(h http.Handler) http.Handler {
_ = req.MultipartForm.RemoveAll() // remove the temp files buffered to tmp directory
}
})
next.ServeHTTP(context.WrapResponseWriter(resp), req)
next.ServeHTTP(respWriter, req)
})
}
}
Expand Down
11 changes: 11 additions & 0 deletions routers/web/auth/linkaccount.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var tplLinkAccount templates.TplName = "user/auth/link_account"

// LinkAccount shows the page where the user can decide to login or create a new account
func LinkAccount(ctx *context.Context) {
// FIXME: these common template variables should be prepared in one common function, but not just copy-paste again and again.
ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationPassword || setting.Service.AllowOnlyExternalRegistration
ctx.Data["Title"] = ctx.Tr("link_account")
ctx.Data["LinkAccountMode"] = true
Expand All @@ -43,13 +44,19 @@ func LinkAccount(ctx *context.Context) {
ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm
ctx.Data["ShowRegistrationButton"] = false

// use this to set the right link into the signIn and signUp templates in the link_account template
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/link_account_signin"
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup"

gothUser, ok := ctx.Session.Get("linkAccountGothUser").(goth.User)

// If you'd like to quickly debug the "link account" page layout, just uncomment the blow line
// Don't worry, when the below line exists, the lint won't pass: ineffectual assignment to gothUser (ineffassign)
// gothUser, ok = goth.User{Email: "invalid-email", Name: "."}, true // intentionally use invalid data to avoid pass the registration check

if !ok {
// no account in session, so just redirect to the login page, then the user could restart the process
ctx.Redirect(setting.AppSubURL + "/user/login")
Expand Down Expand Up @@ -135,6 +142,8 @@ func LinkAccountPostSignIn(ctx *context.Context) {
ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm
ctx.Data["ShowRegistrationButton"] = false

// use this to set the right link into the signIn and signUp templates in the link_account template
Expand Down Expand Up @@ -223,6 +232,8 @@ func LinkAccountPostRegister(ctx *context.Context) {
ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm
ctx.Data["ShowRegistrationButton"] = false

// use this to set the right link into the signIn and signUp templates in the link_account template
Expand Down
103 changes: 59 additions & 44 deletions services/context/access_log.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ import (
"code.gitea.io/gitea/modules/web/middleware"
)

type routerLoggerOptions struct {
req *http.Request
type accessLoggerTmplData struct {
Identity *string
Start *time.Time
ResponseWriter http.ResponseWriter
Ctx map[string]any
RequestID *string
ResponseWriter struct {
Status, Size int
}
Ctx map[string]any
RequestID *string
}

const keyOfRequestIDInTemplate = ".RequestID"
Expand All @@ -51,51 +52,65 @@ func parseRequestIDFromRequestHeader(req *http.Request) string {
return requestID
}

type accessLogRecorder struct {
logger log.BaseLogger
logTemplate *template.Template
needRequestID bool
}

func (lr *accessLogRecorder) record(start time.Time, respWriter ResponseWriter, req *http.Request) {
var requestID string
if lr.needRequestID {
requestID = parseRequestIDFromRequestHeader(req)
}

reqHost, _, err := net.SplitHostPort(req.RemoteAddr)
if err != nil {
reqHost = req.RemoteAddr
}

identity := "-"
data := middleware.GetContextData(req.Context())
if signedUser, ok := data[middleware.ContextDataKeySignedUser].(*user_model.User); ok {
identity = signedUser.Name
}
buf := bytes.NewBuffer([]byte{})
tmplData := accessLoggerTmplData{
Identity: &identity,
Start: &start,
Ctx: map[string]any{
"RemoteAddr": req.RemoteAddr,
"RemoteHost": reqHost,
"Req": req,
},
RequestID: &requestID,
}
tmplData.ResponseWriter.Status = respWriter.Status()
tmplData.ResponseWriter.Size = respWriter.WrittenSize()
err = lr.logTemplate.Execute(buf, tmplData)
if err != nil {
log.Error("Could not execute access logger template: %v", err.Error())
}

lr.logger.Log(1, log.INFO, "%s", buf.String())
}

func newAccessLogRecorder() *accessLogRecorder {
return &accessLogRecorder{
logger: log.GetLogger("access"),
logTemplate: template.Must(template.New("log").Parse(setting.Log.AccessLogTemplate)),
needRequestID: len(setting.Log.RequestIDHeaders) > 0 && strings.Contains(setting.Log.AccessLogTemplate, keyOfRequestIDInTemplate),
}
}

// AccessLogger returns a middleware to log access logger
func AccessLogger() func(http.Handler) http.Handler {
logger := log.GetLogger("access")
needRequestID := len(setting.Log.RequestIDHeaders) > 0 && strings.Contains(setting.Log.AccessLogTemplate, keyOfRequestIDInTemplate)
logTemplate, _ := template.New("log").Parse(setting.Log.AccessLogTemplate)
recorder := newAccessLogRecorder()
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
start := time.Now()

var requestID string
if needRequestID {
requestID = parseRequestIDFromRequestHeader(req)
}

reqHost, _, err := net.SplitHostPort(req.RemoteAddr)
if err != nil {
reqHost = req.RemoteAddr
}

next.ServeHTTP(w, req)
rw := w.(ResponseWriter)

identity := "-"
data := middleware.GetContextData(req.Context())
if signedUser, ok := data[middleware.ContextDataKeySignedUser].(*user_model.User); ok {
identity = signedUser.Name
}
buf := bytes.NewBuffer([]byte{})
err = logTemplate.Execute(buf, routerLoggerOptions{
req: req,
Identity: &identity,
Start: &start,
ResponseWriter: rw,
Ctx: map[string]any{
"RemoteAddr": req.RemoteAddr,
"RemoteHost": reqHost,
"Req": req,
},
RequestID: &requestID,
})
if err != nil {
log.Error("Could not execute access logger template: %v", err.Error())
}

logger.Info("%s", buf.String())
recorder.record(start, w.(ResponseWriter), req)
})
}
}
75 changes: 75 additions & 0 deletions services/context/access_log_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package context

import (
"fmt"
"net/http"
"net/url"
"testing"
"time"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"

"github.com/stretchr/testify/assert"
)

type testAccessLoggerMock struct {
logs []string
}

func (t *testAccessLoggerMock) Log(skip int, level log.Level, format string, v ...any) {
t.logs = append(t.logs, fmt.Sprintf(format, v...))
}

func (t *testAccessLoggerMock) GetLevel() log.Level {
return log.INFO
}

type testAccessLoggerResponseWriterMock struct{}

func (t testAccessLoggerResponseWriterMock) Header() http.Header {
return nil
}

func (t testAccessLoggerResponseWriterMock) Before(f func(ResponseWriter)) {}

func (t testAccessLoggerResponseWriterMock) WriteHeader(statusCode int) {}

func (t testAccessLoggerResponseWriterMock) Write(bytes []byte) (int, error) {
return 0, nil
}

func (t testAccessLoggerResponseWriterMock) Flush() {}

func (t testAccessLoggerResponseWriterMock) WrittenStatus() int {
return http.StatusOK
}

func (t testAccessLoggerResponseWriterMock) Status() int {
return t.WrittenStatus()
}

func (t testAccessLoggerResponseWriterMock) WrittenSize() int {
return 123123
}

func TestAccessLogger(t *testing.T) {
setting.Log.AccessLogTemplate = `{{.Ctx.RemoteHost}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}" "{{.Ctx.Req.UserAgent}}"`
recorder := newAccessLogRecorder()
mockLogger := &testAccessLoggerMock{}
recorder.logger = mockLogger
req := &http.Request{
RemoteAddr: "remote-addr",
Method: "GET",
Proto: "https",
URL: &url.URL{Path: "/path"},
}
req.Header = http.Header{}
req.Header.Add("Referer", "referer")
req.Header.Add("User-Agent", "user-agent")
recorder.record(time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC), &testAccessLoggerResponseWriterMock{}, req)
assert.Equal(t, []string{`remote-addr - - [02/Jan/2000:03:04:05 +0000] "GET /path https" 200 123123 "referer" "user-agent"`}, mockLogger.logs)
}
Loading

0 comments on commit 4ebb00a

Please sign in to comment.