This commit is contained in:
@@ -4,15 +4,17 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"strings"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/actions"
|
||||
"code.gitea.io/gitea/modules/httplib"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
actions_model "gitea.dev/models/actions"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/modules/actions"
|
||||
"gitea.dev/modules/httplib"
|
||||
"gitea.dev/modules/util"
|
||||
"gitea.dev/services/context"
|
||||
)
|
||||
|
||||
func DownloadActionsRunJobLogsWithID(ctx *context.Base, ctxRepo *repo_model.Repository, runID, jobID int64) error {
|
||||
@@ -31,7 +33,8 @@ func DownloadActionsRunJobLogs(ctx *context.Base, ctxRepo *repo_model.Repository
|
||||
return util.NewNotExistErrorf("job not found")
|
||||
}
|
||||
|
||||
if curJob.TaskID == 0 {
|
||||
taskID := curJob.EffectiveTaskID()
|
||||
if taskID == 0 {
|
||||
return util.NewNotExistErrorf("job not started")
|
||||
}
|
||||
|
||||
@@ -39,7 +42,7 @@ func DownloadActionsRunJobLogs(ctx *context.Base, ctxRepo *repo_model.Repository
|
||||
return fmt.Errorf("LoadRun: %w", err)
|
||||
}
|
||||
|
||||
task, err := actions_model.GetTaskByID(ctx, curJob.TaskID)
|
||||
task, err := actions_model.GetTaskByID(ctx, taskID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetTaskByID: %w", err)
|
||||
}
|
||||
@@ -50,6 +53,9 @@ func DownloadActionsRunJobLogs(ctx *context.Base, ctxRepo *repo_model.Repository
|
||||
|
||||
reader, err := actions.OpenLogs(ctx, task.LogInStorage, task.LogFilename)
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return util.NewNotExistErrorf("logs not found")
|
||||
}
|
||||
return fmt.Errorf("OpenLogs: %w", err)
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
auth_service "code.gitea.io/gitea/services/auth"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/web/middleware"
|
||||
auth_service "gitea.dev/services/auth"
|
||||
"gitea.dev/services/context"
|
||||
)
|
||||
|
||||
type AuthResult struct {
|
||||
|
||||
@@ -7,10 +7,11 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/reqctx"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/reqctx"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/web/middleware"
|
||||
"gitea.dev/modules/web/routing"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
@@ -71,10 +72,6 @@ func isRoutePathExpensive(routePattern string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func isRoutePathForLongPolling(routePattern string) bool {
|
||||
return routePattern == "/user/events"
|
||||
}
|
||||
|
||||
func determineRequestPriority(reqCtx reqctx.RequestContext) (ret struct {
|
||||
SignedIn bool
|
||||
Expensive bool
|
||||
@@ -86,7 +83,7 @@ func determineRequestPriority(reqCtx reqctx.RequestContext) (ret struct {
|
||||
ret.SignedIn = true
|
||||
} else {
|
||||
ret.Expensive = isRoutePathExpensive(chiRoutePath)
|
||||
ret.LongPolling = isRoutePathForLongPolling(chiRoutePath)
|
||||
ret.LongPolling = routing.GetRequestRecordInfo(reqCtx).IsLongPolling
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
@@ -25,6 +25,4 @@ func TestBlockExpensive(t *testing.T) {
|
||||
for _, c := range cases {
|
||||
assert.Equal(t, c.expensive, isRoutePathExpensive(c.routePath), "routePath: %s", c.routePath)
|
||||
}
|
||||
|
||||
assert.True(t, isRoutePathForLongPolling("/user/events"))
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/indexer"
|
||||
code_indexer "code.gitea.io/gitea/modules/indexer/code"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
"gitea.dev/modules/indexer"
|
||||
code_indexer "gitea.dev/modules/indexer/code"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/services/context"
|
||||
)
|
||||
|
||||
func PrepareCodeSearch(ctx *context.Context) (ret struct {
|
||||
|
||||
@@ -5,12 +5,14 @@ package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/git"
|
||||
"gitea.dev/modules/util"
|
||||
)
|
||||
|
||||
type CompareRouterReq struct {
|
||||
@@ -19,9 +21,10 @@ type CompareRouterReq struct {
|
||||
|
||||
CompareSeparator string
|
||||
|
||||
HeadOwner string
|
||||
HeadRepoName string
|
||||
HeadOriRef string
|
||||
HeadOwner string
|
||||
HeadRepoName string
|
||||
HeadOriRef string
|
||||
HeadOriRefSuffix string
|
||||
}
|
||||
|
||||
func (cr *CompareRouterReq) DirectComparison() bool {
|
||||
@@ -79,9 +82,11 @@ func ParseCompareRouterParam(routerParam string) *CompareRouterReq {
|
||||
sep = ".."
|
||||
basePart, headPart, ok = strings.Cut(routerParam, sep)
|
||||
if !ok {
|
||||
headOwnerName, headRepoName, headRef := parseHead(routerParam)
|
||||
headOwnerName, headRepoName, headOriRef := parseHead(routerParam)
|
||||
headOriRef, headOriRefSuffix := git.ParseRefSuffix(headOriRef)
|
||||
return &CompareRouterReq{
|
||||
HeadOriRef: headRef,
|
||||
HeadOriRef: headOriRef,
|
||||
HeadOriRefSuffix: headOriRefSuffix,
|
||||
HeadOwner: headOwnerName,
|
||||
HeadRepoName: headRepoName,
|
||||
CompareSeparator: "...",
|
||||
@@ -92,9 +97,36 @@ func ParseCompareRouterParam(routerParam string) *CompareRouterReq {
|
||||
ci := &CompareRouterReq{CompareSeparator: sep}
|
||||
ci.BaseOriRef, ci.BaseOriRefSuffix = git.ParseRefSuffix(basePart)
|
||||
ci.HeadOwner, ci.HeadRepoName, ci.HeadOriRef = parseHead(headPart)
|
||||
ci.HeadOriRef, ci.HeadOriRefSuffix = git.ParseRefSuffix(ci.HeadOriRef)
|
||||
return ci
|
||||
}
|
||||
|
||||
// validRefSuffix matches only ^/~ ancestry navigation. The ^{...}, @{...} and :path forms address
|
||||
// other objects (trees, blobs) or reflog/upstream state that compare does not resolve, so they are rejected.
|
||||
var validRefSuffix = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`^(?:[~^][0-9]*)+$`)
|
||||
})
|
||||
|
||||
// ResolveRefWithSuffix resolves oriRef plus an optional revision suffix (^, ~N) to a RefName.
|
||||
// A nil error guarantees a usable RefName: an unsupported suffix yields an invalid-argument error
|
||||
// and an unresolvable ref yields a not-found error.
|
||||
func ResolveRefWithSuffix(gitRepo *git.Repository, oriRef, refSuffix string) (git.RefName, error) {
|
||||
if refSuffix == "" {
|
||||
if refName := gitRepo.UnstableGuessRefByShortName(oriRef); refName != "" {
|
||||
return refName, nil
|
||||
}
|
||||
return "", util.NewNotExistErrorf("ref %q does not exist", oriRef)
|
||||
}
|
||||
if !validRefSuffix().MatchString(refSuffix) {
|
||||
return "", util.NewInvalidArgumentErrorf("unsupported ref suffix %q", refSuffix)
|
||||
}
|
||||
commit, err := gitRepo.GetCommit(oriRef + refSuffix)
|
||||
if err != nil {
|
||||
return "", util.NewNotExistErrorf("ref %q does not exist", oriRef+refSuffix)
|
||||
}
|
||||
return git.RefNameFromCommit(commit.ID.String()), nil
|
||||
}
|
||||
|
||||
// maxForkTraverseLevel defines the maximum levels to traverse when searching for the head repository.
|
||||
const maxForkTraverseLevel = 10
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ package common
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -97,9 +99,56 @@ func TestCompareRouterReq(t *testing.T) {
|
||||
HeadOriRef: "develop",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "main...develop^",
|
||||
CompareRouterReq: &CompareRouterReq{
|
||||
BaseOriRef: "main",
|
||||
CompareSeparator: "...",
|
||||
HeadOriRef: "develop",
|
||||
HeadOriRefSuffix: "^",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "main~2...develop",
|
||||
CompareRouterReq: &CompareRouterReq{
|
||||
BaseOriRef: "main",
|
||||
BaseOriRefSuffix: "~2",
|
||||
CompareSeparator: "...",
|
||||
HeadOriRef: "develop",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "main...lunny/forked_repo:develop~3",
|
||||
CompareRouterReq: &CompareRouterReq{
|
||||
BaseOriRef: "main",
|
||||
CompareSeparator: "...",
|
||||
HeadOwner: "lunny",
|
||||
HeadRepoName: "forked_repo",
|
||||
HeadOriRef: "develop",
|
||||
HeadOriRefSuffix: "~3",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "develop^",
|
||||
CompareRouterReq: &CompareRouterReq{
|
||||
CompareSeparator: "...",
|
||||
HeadOriRef: "develop",
|
||||
HeadOriRefSuffix: "^",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
assert.Equal(t, c.CompareRouterReq, ParseCompareRouterParam(c.input), "input: %s", c.input)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveRefWithSuffix(t *testing.T) {
|
||||
// The ^{...}, @{...} and :path forms address non-commit objects or reflog state, so they are
|
||||
// rejected before any repository access and a nil repo is fine here.
|
||||
for _, refSuffix := range []string{"^{/Add}", "^{commit}", "@{upstream}", "~1:path"} {
|
||||
ref, err := ResolveRefWithSuffix(nil, "branch", refSuffix)
|
||||
assert.ErrorIs(t, err, util.ErrInvalidArgument, "suffix %q", refSuffix)
|
||||
assert.Empty(t, ref, "suffix %q", refSuffix)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,15 +8,13 @@ import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/migrations"
|
||||
system_model "code.gitea.io/gitea/models/system"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/setting/config"
|
||||
"code.gitea.io/gitea/services/versioned_migration"
|
||||
|
||||
"xorm.io/xorm"
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/models/migrations"
|
||||
system_model "gitea.dev/models/system"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/setting/config"
|
||||
"gitea.dev/services/versioned_migration"
|
||||
)
|
||||
|
||||
// InitDBEngine In case of problems connecting to DB, retry connection. Eg, PGSQL in Docker Container on Synology
|
||||
@@ -42,7 +40,7 @@ func InitDBEngine(ctx context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrateWithSetting(ctx context.Context, x *xorm.Engine) error {
|
||||
func migrateWithSetting(ctx context.Context, x db.EngineMigration) error {
|
||||
if setting.Database.AutoMigration {
|
||||
return versioned_migration.Migrate(ctx, x)
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ package common
|
||||
import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/timeutil"
|
||||
)
|
||||
|
||||
func ParseDeadlineDateToEndOfDay(date string) (timeutil.TimeStamp, error) {
|
||||
|
||||
@@ -10,15 +10,15 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/httpcache"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/reqctx"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
"code.gitea.io/gitea/modules/web/routing"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/httpcache"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/reqctx"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/templates"
|
||||
"gitea.dev/modules/web/middleware"
|
||||
"gitea.dev/modules/web/routing"
|
||||
"gitea.dev/services/context"
|
||||
)
|
||||
|
||||
const tplStatus500 templates.TplName = "status/500"
|
||||
@@ -32,11 +32,7 @@ func renderServerErrorPage(w http.ResponseWriter, req *http.Request, respCode in
|
||||
}
|
||||
}
|
||||
|
||||
httpcache.SetCacheControlInHeader(w.Header(), &httpcache.CacheControlOptions{NoTransform: true})
|
||||
if setting.Security.XFrameOptions != "unset" {
|
||||
w.Header().Set(`X-Frame-Options`, setting.Security.XFrameOptions)
|
||||
}
|
||||
|
||||
httpcache.SetCacheControlInHeader(w.Header(), &httpcache.CacheControlOptions{})
|
||||
tmplCtx := context.NewTemplateContextForWeb(reqctx.FromContext(req.Context()), req, middleware.Locale(w, req))
|
||||
w.WriteHeader(respCode)
|
||||
|
||||
@@ -44,6 +40,7 @@ func renderServerErrorPage(w http.ResponseWriter, req *http.Request, respCode in
|
||||
if acceptsHTML {
|
||||
err := templates.PageRenderer().HTML(outBuf, respCode, tmpl, ctxData, tmplCtx)
|
||||
if err != nil {
|
||||
log.Error("Failed to render error page template %s: %v", tmpl, err)
|
||||
_, _ = w.Write([]byte("Internal server error but failed to render error page template, please collect error logs and report to Gitea issue tracker"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/reqctx"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"gitea.dev/models/unittest"
|
||||
"gitea.dev/modules/reqctx"
|
||||
"gitea.dev/modules/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -4,7 +4,13 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"context"
|
||||
|
||||
"gitea.dev/models/organization"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/optional"
|
||||
"gitea.dev/modules/util"
|
||||
)
|
||||
|
||||
func ParseIssueFilterStateIsClosed(state string) optional.Option[bool] {
|
||||
@@ -23,3 +29,59 @@ func ParseIssueFilterStateIsClosed(state string) optional.Option[bool] {
|
||||
func ParseIssueFilterTypeIsPull(typ string) optional.Option[bool] {
|
||||
return optional.FromMapLookup(map[string]bool{"pulls": true, "issues": false}, typ)
|
||||
}
|
||||
|
||||
type SearchIssuesRepoIDsOptions struct {
|
||||
Doer *user_model.User
|
||||
PublicOnly bool
|
||||
OwnerName string
|
||||
TeamName string
|
||||
}
|
||||
|
||||
// SearchIssuesRepoIDs resolves the repository filter of an issue search. allPublic makes the indexer
|
||||
// match everything its own is_public covers (modules/indexer/issues/util.go), so repoIDs omits those.
|
||||
func SearchIssuesRepoIDs(ctx context.Context, opts SearchIssuesRepoIDsOptions) (repoIDs []int64, allPublic bool, err error) {
|
||||
searchOpts := repo_model.SearchRepoOptions{
|
||||
Private: opts.Doer != nil,
|
||||
Collaborate: optional.None[bool](),
|
||||
Actor: opts.Doer,
|
||||
}
|
||||
searchOpts.ApplyPublicOnly(opts.PublicOnly)
|
||||
if opts.OwnerName != "" {
|
||||
owner, err := user_model.GetUserByName(ctx, opts.OwnerName)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
searchOpts.OwnerID = owner.ID
|
||||
searchOpts.Collaborate = optional.Some(false)
|
||||
}
|
||||
if opts.TeamName != "" {
|
||||
if opts.OwnerName == "" {
|
||||
return nil, false, util.NewInvalidArgumentErrorf("owner organisation is required for filtering on team")
|
||||
}
|
||||
team, err := organization.GetTeam(ctx, searchOpts.OwnerID, opts.TeamName)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
searchOpts.TeamID = team.ID
|
||||
}
|
||||
|
||||
// SearchRepoOptions.AllPublic and AllLimited only apply under an owner filter, so the indexer covers them
|
||||
allPublic = opts.OwnerName == ""
|
||||
cond := repo_model.SearchRepositoryCondition(searchOpts)
|
||||
if allPublic {
|
||||
if !searchOpts.Private {
|
||||
return []int64{0}, allPublic, nil // sees nothing beyond is_public, so skip the query
|
||||
}
|
||||
cond = cond.And(repo_model.NotPublicRepoUnderPublicOwnerCond()) // enumerating them scales with the instance
|
||||
}
|
||||
repoIDs, err = repo_model.SearchRepositoryIDsByCondition(ctx, cond)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if len(repoIDs) == 0 {
|
||||
// no repos found, don't let the indexer return all repos
|
||||
repoIDs = []int64{0}
|
||||
}
|
||||
|
||||
return repoIDs, allPublic, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gitea.dev/models/unittest"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSearchIssuesRepoIDs(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
// the indexer's is_public covers repo 1 (public under a public owner) but misses repo 38 (public
|
||||
// under a limited org) and repo 40 (public under a private org)
|
||||
cases := []struct {
|
||||
name string
|
||||
doerID int64
|
||||
opts SearchIssuesRepoIDsOptions
|
||||
allPublic bool
|
||||
want []int64
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "site admin", // admins skip the accessible repository condition entirely
|
||||
doerID: 1,
|
||||
allPublic: true,
|
||||
want: []int64{2, 38, 40},
|
||||
},
|
||||
{
|
||||
name: "regular user",
|
||||
doerID: 2,
|
||||
allPublic: true,
|
||||
want: []int64{2, 38},
|
||||
},
|
||||
{
|
||||
name: "private org member",
|
||||
doerID: 5,
|
||||
allPublic: true,
|
||||
want: []int64{38, 40},
|
||||
},
|
||||
{
|
||||
name: "anonymous",
|
||||
allPublic: true,
|
||||
want: []int64{0}, // the placeholder keeps the indexer off "every repository"
|
||||
},
|
||||
{
|
||||
name: "public-only token",
|
||||
doerID: 2,
|
||||
opts: SearchIssuesRepoIDsOptions{PublicOnly: true},
|
||||
allPublic: true,
|
||||
want: []int64{0},
|
||||
},
|
||||
{
|
||||
name: "owner filter", // turns allPublic off, so public repos must still be enumerated
|
||||
doerID: 2,
|
||||
opts: SearchIssuesRepoIDsOptions{OwnerName: "user2"},
|
||||
want: []int64{1, 2},
|
||||
},
|
||||
{
|
||||
name: "team without owner",
|
||||
opts: SearchIssuesRepoIDsOptions{TeamName: "team1"},
|
||||
wantErr: util.ErrInvalidArgument,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
opts := tc.opts
|
||||
if tc.doerID != 0 {
|
||||
opts.Doer = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: tc.doerID})
|
||||
}
|
||||
|
||||
repoIDs, allPublic, err := SearchIssuesRepoIDs(t.Context(), opts)
|
||||
if tc.wantErr != nil {
|
||||
assert.ErrorIs(t, err, tc.wantErr)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.allPublic, allPublic)
|
||||
assert.Subset(t, repoIDs, tc.want)
|
||||
if allPublic {
|
||||
assert.NotContains(t, repoIDs, int64(1), "already matched by the indexer's is_public")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ package common
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/services/lfs"
|
||||
"gitea.dev/modules/web"
|
||||
"gitea.dev/services/lfs"
|
||||
)
|
||||
|
||||
const RouterMockPointCommonLFS = "common-lfs"
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"gitea.dev/modules/container"
|
||||
"gitea.dev/modules/setting"
|
||||
)
|
||||
|
||||
func MaintenanceModeHandler() func(h http.Handler) http.Handler {
|
||||
|
||||
@@ -10,14 +10,14 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/renderhelper"
|
||||
"code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
"gitea.dev/models/renderhelper"
|
||||
"gitea.dev/models/repo"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/markup"
|
||||
"gitea.dev/modules/markup/markdown"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/util"
|
||||
"gitea.dev/services/context"
|
||||
)
|
||||
|
||||
// RenderMarkup renders markup text for the /markup and /markdown endpoints
|
||||
@@ -26,9 +26,13 @@ func RenderMarkup(ctx *context.Base, ctxRepo *context.Repository, mode, text, ur
|
||||
// filePath is the path of the file to render if the end user is trying to preview a repo file (mode == "file")
|
||||
// filePath will be used as RenderContext.RelativePath
|
||||
|
||||
// TODO: MARKUP-RENDER-CONTEXT: this logic is unnecessarily complicated.
|
||||
// Ideally: the "file path" should not appear in the "url path context", but it needs a lot of refactoring to achieve that
|
||||
// for example, when previewing file "/gitea/owner/repo/src/branch/features/feat-123/doc/CHANGE.md", then filePath is "doc/CHANGE.md"
|
||||
// and the urlPathContext is "/gitea/owner/repo/src/branch/features/feat-123/doc"
|
||||
|
||||
ctx.SetHeaderContentSecurityPolicyGeneral()
|
||||
|
||||
if mode == "" || mode == "markdown" {
|
||||
// raw Markdown doesn't do any special handling
|
||||
// TODO: raw markdown doesn't do any link processing, so "urlPathContext" doesn't take effect
|
||||
@@ -60,6 +64,7 @@ func RenderMarkup(ctx *context.Base, ctxRepo *context.Repository, mode, text, ur
|
||||
treePath = path.Dir(filePath) // it is "doc" if filePath is "doc/CHANGE.md"
|
||||
refPath = strings.Join(fields[3:], "/") // it is "branch/features/feat-12/doc"
|
||||
refPath = strings.TrimSuffix(refPath, "/"+treePath) // now we get the correct branch path: "branch/features/feat-12"
|
||||
refPath = util.PathEscapeSegments(refPath)
|
||||
} else if fields = strings.SplitN(repoLinkPath, "/", 3); len(fields) == 2 {
|
||||
repoOwnerName, repoName = fields[0], fields[1]
|
||||
}
|
||||
@@ -69,7 +74,7 @@ func RenderMarkup(ctx *context.Base, ctxRepo *context.Repository, mode, text, ur
|
||||
case "gfm": // legacy mode
|
||||
rctx = renderhelper.NewRenderContextRepoFile(ctx, repoModel, renderhelper.RepoFileOptions{
|
||||
DeprecatedOwnerName: repoOwnerName, DeprecatedRepoName: repoName,
|
||||
CurrentRefPath: refPath, CurrentTreePath: treePath,
|
||||
CurrentRefSubURL: refPath, CurrentTreePath: treePath,
|
||||
})
|
||||
rctx = rctx.WithMarkupType(markdown.MarkupName)
|
||||
case "comment":
|
||||
@@ -85,7 +90,7 @@ func RenderMarkup(ctx *context.Base, ctxRepo *context.Repository, mode, text, ur
|
||||
case "file":
|
||||
rctx = renderhelper.NewRenderContextRepoFile(ctx, repoModel, renderhelper.RepoFileOptions{
|
||||
DeprecatedOwnerName: repoOwnerName, DeprecatedRepoName: repoName,
|
||||
CurrentRefPath: refPath, CurrentTreePath: treePath,
|
||||
CurrentRefSubURL: refPath, CurrentTreePath: treePath,
|
||||
})
|
||||
rctx = rctx.WithMarkupType("").WithRelativePath(filePath) // render the repo file content by its extension
|
||||
default:
|
||||
|
||||
@@ -8,15 +8,15 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
"code.gitea.io/gitea/modules/gtprof"
|
||||
"code.gitea.io/gitea/modules/httplib"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/public"
|
||||
"code.gitea.io/gitea/modules/reqctx"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/web/routing"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
"gitea.dev/modules/cache"
|
||||
"gitea.dev/modules/gtprof"
|
||||
"gitea.dev/modules/httplib"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/public"
|
||||
"gitea.dev/modules/reqctx"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/web/routing"
|
||||
"gitea.dev/services/context"
|
||||
|
||||
"gitea.com/go-chi/session"
|
||||
"github.com/chi-middleware/proxy"
|
||||
@@ -28,14 +28,13 @@ func ProtocolMiddlewares() (handlers []any) {
|
||||
// the order is important
|
||||
handlers = append(handlers, ChiRoutePathHandler()) // make sure chi has correct paths
|
||||
handlers = append(handlers, RequestContextHandler()) // prepare the context and panic recovery
|
||||
handlers = append(handlers, SecurityHeadersHandler())
|
||||
|
||||
if setting.ReverseProxyLimit > 0 && len(setting.ReverseProxyTrustedProxies) > 0 {
|
||||
handlers = append(handlers, ForwardedHeadersHandler(setting.ReverseProxyLimit, setting.ReverseProxyTrustedProxies))
|
||||
}
|
||||
|
||||
if setting.IsRouteLogEnabled() {
|
||||
handlers = append(handlers, routing.NewLoggerHandler())
|
||||
}
|
||||
handlers = append(handlers, routing.NewRequestInfoHandler())
|
||||
|
||||
if setting.IsAccessLogEnabled() {
|
||||
handlers = append(handlers, context.AccessLogger())
|
||||
@@ -48,6 +47,21 @@ func ProtocolMiddlewares() (handlers []any) {
|
||||
return handlers
|
||||
}
|
||||
|
||||
// SecurityHeadersHandler sets headers globally for every response that leaves Gitea.
|
||||
func SecurityHeadersHandler() func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
if setting.Security.XContentTypeOptions != "unset" {
|
||||
resp.Header().Set("X-Content-Type-Options", setting.Security.XContentTypeOptions)
|
||||
}
|
||||
if setting.Security.XFrameOptions != "unset" {
|
||||
resp.Header().Set("X-Frame-Options", setting.Security.XFrameOptions)
|
||||
}
|
||||
next.ServeHTTP(resp, req)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func RequestContextHandler() func(h http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(respOrig http.ResponseWriter, req *http.Request) {
|
||||
@@ -133,6 +147,9 @@ func MustInitSessioner() func(next http.Handler) http.Handler {
|
||||
Secure: setting.SessionConfig.Secure,
|
||||
SameSite: setting.SessionConfig.SameSite,
|
||||
Domain: setting.SessionConfig.Domain,
|
||||
|
||||
// in the future, if websocket is used, the websocket handler should manage its own session sync (release)
|
||||
IgnoreReleaseForWebSocket: true,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal("common.Sessioner failed: %v", err)
|
||||
|
||||
@@ -8,11 +8,11 @@ import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
activities_model "gitea.dev/models/activities"
|
||||
"gitea.dev/models/db"
|
||||
issues_model "gitea.dev/models/issues"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/services/context"
|
||||
)
|
||||
|
||||
// StopwatchTmplInfo is a view on a stopwatch specifically for template rendering
|
||||
|
||||
@@ -9,11 +9,12 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/templates"
|
||||
"gitea.dev/modules/web/middleware"
|
||||
"gitea.dev/modules/web/routing"
|
||||
|
||||
"github.com/bohde/codel"
|
||||
"github.com/go-chi/chi/v5"
|
||||
@@ -68,7 +69,7 @@ func QoS() func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
ctx := req.Context()
|
||||
|
||||
reqRecordInfo := routing.GetRequestRecordInfo(ctx)
|
||||
priority := requestPriority(ctx)
|
||||
|
||||
// Check if the request can begin processing.
|
||||
@@ -79,9 +80,8 @@ func QoS() func(next http.Handler) http.Handler {
|
||||
return
|
||||
}
|
||||
|
||||
// Release long-polling immediately, so they don't always
|
||||
// take up an in-flight request
|
||||
if strings.Contains(req.URL.Path, "/user/events") {
|
||||
// Release long-polling immediately, so they don't always take up an in-flight request
|
||||
if reqRecordInfo.IsLongPolling {
|
||||
c.Release()
|
||||
} else {
|
||||
defer c.Release()
|
||||
|
||||
@@ -6,9 +6,9 @@ package common
|
||||
import (
|
||||
"testing"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
"code.gitea.io/gitea/services/contexttest"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/web/middleware"
|
||||
"gitea.dev/services/contexttest"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -6,7 +6,7 @@ package common
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/httplib"
|
||||
"gitea.dev/modules/httplib"
|
||||
)
|
||||
|
||||
// FetchRedirectDelegate helps the "fetch" requests to redirect to the correct location
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -7,13 +7,13 @@ import (
|
||||
"path"
|
||||
"time"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/httpcache"
|
||||
"code.gitea.io/gitea/modules/httplib"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/modules/git"
|
||||
"gitea.dev/modules/httpcache"
|
||||
"gitea.dev/modules/httplib"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/structs"
|
||||
"gitea.dev/services/context"
|
||||
)
|
||||
|
||||
// ServeBlob download a git.Blob
|
||||
|
||||
Reference in New Issue
Block a user