mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-24 12:53:53 +02:00
Merge remote-tracking branch 'upstream/main' into limit-repo-size
This commit is contained in:
@@ -217,7 +217,7 @@ func (l *LayeredFS) WatchLocalChanges(ctx context.Context, callback func()) {
|
||||
}
|
||||
layerDirs = append(layerDirs, ".")
|
||||
for _, dir := range layerDirs {
|
||||
if err = watcher.Add(util.FilePathJoinAbs(layer.localPath, dir)); err != nil {
|
||||
if err = watcher.Add(util.FilePathJoinAbs(layer.localPath, dir)); err != nil && !os.IsNotExist(err) {
|
||||
log.Error("Unable to watch directory %s: %v", dir, err)
|
||||
}
|
||||
}
|
||||
|
||||
+36
-23
@@ -17,8 +17,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/avatar/identicon"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/nfnt/resize"
|
||||
"github.com/oliamb/cutter"
|
||||
"golang.org/x/image/draw"
|
||||
|
||||
_ "golang.org/x/image/webp" // for processing webp images
|
||||
)
|
||||
@@ -81,28 +80,10 @@ func processAvatarImage(data []byte, maxOriginSize int64) ([]byte, error) {
|
||||
}
|
||||
|
||||
// try to crop and resize the origin image if necessary
|
||||
if imgCfg.Width != imgCfg.Height {
|
||||
var newSize, ax, ay int
|
||||
if imgCfg.Width > imgCfg.Height {
|
||||
newSize = imgCfg.Height
|
||||
ax = (imgCfg.Width - imgCfg.Height) / 2
|
||||
} else {
|
||||
newSize = imgCfg.Width
|
||||
ay = (imgCfg.Height - imgCfg.Width) / 2
|
||||
}
|
||||
img = cropSquare(img)
|
||||
|
||||
img, err = cutter.Crop(img, cutter.Config{
|
||||
Width: newSize,
|
||||
Height: newSize,
|
||||
Anchor: image.Point{X: ax, Y: ay},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
targetSize := uint(DefaultAvatarSize * setting.Avatar.RenderedSizeFactor)
|
||||
img = resize.Resize(targetSize, targetSize, img, resize.Bilinear)
|
||||
targetSize := DefaultAvatarSize * setting.Avatar.RenderedSizeFactor
|
||||
img = scale(img, targetSize, targetSize, draw.BiLinear)
|
||||
|
||||
// try to encode the cropped/resized image to png
|
||||
bs := bytes.Buffer{}
|
||||
@@ -124,3 +105,35 @@ func processAvatarImage(data []byte, maxOriginSize int64) ([]byte, error) {
|
||||
func ProcessAvatarImage(data []byte) ([]byte, error) {
|
||||
return processAvatarImage(data, setting.Avatar.MaxOriginSize)
|
||||
}
|
||||
|
||||
// scale resizes the image to width x height using the given scaler.
|
||||
func scale(src image.Image, width, height int, scale draw.Scaler) image.Image {
|
||||
rect := image.Rect(0, 0, width, height)
|
||||
dst := image.NewRGBA(rect)
|
||||
scale.Scale(dst, rect, src, src.Bounds(), draw.Over, nil)
|
||||
return dst
|
||||
}
|
||||
|
||||
// cropSquare crops the largest square image from the center of the image.
|
||||
// If the image is already square, it is returned unchanged.
|
||||
func cropSquare(src image.Image) image.Image {
|
||||
bounds := src.Bounds()
|
||||
if bounds.Dx() == bounds.Dy() {
|
||||
return src
|
||||
}
|
||||
|
||||
var rect image.Rectangle
|
||||
if bounds.Dx() > bounds.Dy() {
|
||||
// width > height
|
||||
size := bounds.Dy()
|
||||
rect = image.Rect((bounds.Dx()-size)/2, 0, (bounds.Dx()+size)/2, size)
|
||||
} else {
|
||||
// width < height
|
||||
size := bounds.Dx()
|
||||
rect = image.Rect(0, (bounds.Dy()-size)/2, size, (bounds.Dy()+size)/2)
|
||||
}
|
||||
|
||||
dst := image.NewRGBA(rect)
|
||||
draw.Draw(dst, rect, src, rect.Min, draw.Src)
|
||||
return dst
|
||||
}
|
||||
|
||||
Vendored
+6
-6
@@ -11,13 +11,13 @@ import (
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
|
||||
mc "gitea.com/go-chi/cache"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
lru "github.com/hashicorp/golang-lru/v2"
|
||||
)
|
||||
|
||||
// TwoQueueCache represents a LRU 2Q cache adapter implementation
|
||||
type TwoQueueCache struct {
|
||||
lock sync.Mutex
|
||||
cache *lru.TwoQueueCache
|
||||
cache *lru.TwoQueueCache[string, any]
|
||||
interval int
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ func (c *TwoQueueCache) Flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *TwoQueueCache) checkAndInvalidate(key any) {
|
||||
func (c *TwoQueueCache) checkAndInvalidate(key string) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
cached, ok := c.cache.Peek(key)
|
||||
@@ -155,7 +155,7 @@ func (c *TwoQueueCache) checkAndInvalidate(key any) {
|
||||
}
|
||||
item, ok := cached.(*MemoryItem)
|
||||
if !ok || item.hasExpired() {
|
||||
c.cache.Remove(item)
|
||||
c.cache.Remove(key)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,9 +187,9 @@ func (c *TwoQueueCache) StartAndGC(opts mc.Options) error {
|
||||
GhostRatio: lru.Default2QGhostEntries,
|
||||
}
|
||||
_ = json.Unmarshal([]byte(opts.AdapterConfig), cfg)
|
||||
c.cache, err = lru.New2QParams(cfg.Size, cfg.RecentRatio, cfg.GhostRatio)
|
||||
c.cache, err = lru.New2QParams[string, any](cfg.Size, cfg.RecentRatio, cfg.GhostRatio)
|
||||
} else {
|
||||
c.cache, err = lru.New2Q(size)
|
||||
c.cache, err = lru.New2Q[string, any](size)
|
||||
}
|
||||
c.interval = opts.Interval
|
||||
if c.interval > 0 {
|
||||
|
||||
@@ -35,7 +35,7 @@ func RequireRepoWriter(unitType unit.Type) func(ctx *Context) {
|
||||
// CanEnableEditor checks if the user is allowed to write to the branch of the repo
|
||||
func CanEnableEditor() func(ctx *Context) {
|
||||
return func(ctx *Context) {
|
||||
if !ctx.Repo.CanWriteToBranch(ctx.Doer, ctx.Repo.BranchName) {
|
||||
if !ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, ctx.Repo.BranchName) {
|
||||
ctx.NotFound("CanWriteToBranch denies permission", nil)
|
||||
return
|
||||
}
|
||||
|
||||
+15
-27
@@ -66,13 +66,13 @@ type Repository struct {
|
||||
}
|
||||
|
||||
// CanWriteToBranch checks if the branch is writable by the user
|
||||
func (r *Repository) CanWriteToBranch(user *user_model.User, branch string) bool {
|
||||
return issues_model.CanMaintainerWriteToBranch(r.Permission, branch, user)
|
||||
func (r *Repository) CanWriteToBranch(ctx context.Context, user *user_model.User, branch string) bool {
|
||||
return issues_model.CanMaintainerWriteToBranch(ctx, r.Permission, branch, user)
|
||||
}
|
||||
|
||||
// CanEnableEditor returns true if repository is editable and user has proper access level.
|
||||
func (r *Repository) CanEnableEditor(user *user_model.User) bool {
|
||||
return r.IsViewBranch && r.CanWriteToBranch(user, r.BranchName) && r.Repository.CanEnableEditor() && !r.Repository.IsArchived
|
||||
func (r *Repository) CanEnableEditor(ctx context.Context, user *user_model.User) bool {
|
||||
return r.IsViewBranch && r.CanWriteToBranch(ctx, user, r.BranchName) && r.Repository.CanEnableEditor() && !r.Repository.IsArchived
|
||||
}
|
||||
|
||||
// CanCreateBranch returns true if repository is editable and user has proper access level.
|
||||
@@ -118,7 +118,7 @@ func (r *Repository) CanCommitToBranch(ctx context.Context, doer *user_model.Use
|
||||
|
||||
sign, keyID, _, err := asymkey_service.SignCRUDAction(ctx, r.Repository.RepoPath(), doer, r.Repository.RepoPath(), git.BranchPrefix+r.BranchName)
|
||||
|
||||
canCommit := r.CanEnableEditor(doer) && userCanPush
|
||||
canCommit := r.CanEnableEditor(ctx, doer) && userCanPush
|
||||
if requireSigned {
|
||||
canCommit = canCommit && sign
|
||||
}
|
||||
@@ -134,7 +134,7 @@ func (r *Repository) CanCommitToBranch(ctx context.Context, doer *user_model.Use
|
||||
|
||||
return CanCommitToBranchResults{
|
||||
CanCommitToBranch: canCommit,
|
||||
EditorEnabled: r.CanEnableEditor(doer),
|
||||
EditorEnabled: r.CanEnableEditor(ctx, doer),
|
||||
UserCanPush: userCanPush,
|
||||
RequireSigned: requireSigned,
|
||||
WillSign: sign,
|
||||
@@ -660,13 +660,6 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
return cancel
|
||||
}
|
||||
|
||||
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetTagNamesByRepoID", err)
|
||||
return cancel
|
||||
}
|
||||
ctx.Data["Tags"] = tags
|
||||
|
||||
branchOpts := git_model.FindBranchOptions{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
IsDeletedBranch: util.OptionalBoolFalse,
|
||||
@@ -680,7 +673,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
return cancel
|
||||
}
|
||||
|
||||
// non empty repo should have at least 1 branch, so this repository's branches haven't been synced yet
|
||||
// non-empty repo should have at least 1 branch, so this repository's branches haven't been synced yet
|
||||
if branchesTotal == 0 { // fallback to do a sync immediately
|
||||
branchesTotal, err = repo_module.SyncRepoBranches(ctx, ctx.Repo.Repository.ID, 0)
|
||||
if err != nil {
|
||||
@@ -689,24 +682,19 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: use paganation and async loading
|
||||
branchOpts.ExcludeBranchNames = []string{ctx.Repo.Repository.DefaultBranch}
|
||||
brs, err := git_model.FindBranchNames(ctx, branchOpts)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetBranches", err)
|
||||
return cancel
|
||||
}
|
||||
// always put default branch on the top
|
||||
ctx.Data["Branches"] = append(branchOpts.ExcludeBranchNames, brs...)
|
||||
ctx.Data["BranchesCount"] = branchesTotal
|
||||
|
||||
// If not branch selected, try default one.
|
||||
// If default branch doesn't exist, fall back to some other branch.
|
||||
// If no branch is set in the request URL, try to guess a default one.
|
||||
if len(ctx.Repo.BranchName) == 0 {
|
||||
if len(ctx.Repo.Repository.DefaultBranch) > 0 && gitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) {
|
||||
ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
|
||||
} else if len(brs) > 0 {
|
||||
ctx.Repo.BranchName = brs[0]
|
||||
} else {
|
||||
ctx.Repo.BranchName, _ = gitRepo.GetDefaultBranch()
|
||||
if ctx.Repo.BranchName == "" {
|
||||
// If it still can't get a default branch, fall back to default branch from setting.
|
||||
// Something might be wrong. Either site admin should fix the repo sync or Gitea should fix a potential bug.
|
||||
ctx.Repo.BranchName = setting.Repository.DefaultBranch
|
||||
}
|
||||
}
|
||||
ctx.Repo.RefName = ctx.Repo.BranchName
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
lru "github.com/hashicorp/golang-lru/v2"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
@@ -130,7 +130,7 @@ func checkEnablePushOptions(ctx context.Context, logger log.Logger, autofix bool
|
||||
func checkDaemonExport(ctx context.Context, logger log.Logger, autofix bool) error {
|
||||
numRepos := 0
|
||||
numNeedUpdate := 0
|
||||
cache, err := lru.New(512)
|
||||
cache, err := lru.New[int64, any](512)
|
||||
if err != nil {
|
||||
logger.Critical("Unable to create cache: %v", err)
|
||||
return err
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
// NewInternalToken generate a new value intended to be used by INTERNAL_TOKEN.
|
||||
|
||||
@@ -7,12 +7,6 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// FileBlame return the Blame object of file
|
||||
func (repo *Repository) FileBlame(revision, path, file string) ([]byte, error) {
|
||||
stdout, _, err := NewCommand(repo.Ctx, "blame", "--root").AddDashesAndList(file).RunStdBytes(&RunOpts{Dir: path})
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
// LineBlame returns the latest commit at the given line
|
||||
func (repo *Repository) LineBlame(revision, path, file string, line uint) (*Commit, error) {
|
||||
res, _, err := NewCommand(repo.Ctx, "blame").
|
||||
|
||||
@@ -150,11 +150,13 @@ func CloseProvidedListeners() error {
|
||||
return returnableError
|
||||
}
|
||||
|
||||
// GetListener obtains a listener for the local network address. The network must be
|
||||
// DefaultGetListener obtains a listener for the local network address. The network must be
|
||||
// a stream-oriented network: "tcp", "tcp4", "tcp6", "unix" or "unixpacket". It
|
||||
// returns an provided net.Listener for the matching network and address, or
|
||||
// creates a new one using net.Listen.
|
||||
func GetListener(network, address string) (net.Listener, error) {
|
||||
// creates a new one using net.Listen. This function can be replaced by changing the
|
||||
// GetListener variable at the top of this file, for example to listen on an onion service using
|
||||
// github.com/cretz/bine
|
||||
func DefaultGetListener(network, address string) (net.Listener, error) {
|
||||
// Add a deferral to say that we've tried to grab a listener
|
||||
defer GetManager().InformCleanup()
|
||||
switch network {
|
||||
|
||||
@@ -9,9 +9,11 @@ package graceful
|
||||
|
||||
import "net"
|
||||
|
||||
// GetListener obtains a listener for the local network address.
|
||||
// On windows this is basically just a shim around net.Listen.
|
||||
func GetListener(network, address string) (net.Listener, error) {
|
||||
// DefaultGetListener obtains a listener for the local network address.
|
||||
// On windows this is basically just a shim around net.Listen. This function
|
||||
// can be replaced by changing the GetListener variable at the top of this file,
|
||||
// for example to listen on an onion service using github.com/cretz/bine
|
||||
func DefaultGetListener(network, address string) (net.Listener, error) {
|
||||
// Add a deferral to say that we've tried to grab a listener
|
||||
defer GetManager().InformCleanup()
|
||||
|
||||
|
||||
@@ -33,6 +33,14 @@ var (
|
||||
PerWriteWriteTimeoutKbTime = 10 * time.Second
|
||||
)
|
||||
|
||||
// GetListener returns a listener from a GetListener function, which must have the
|
||||
// signature: `func FunctioName(network, address string) (net.Listener, error)`.
|
||||
// This determines the implementation of net.Listener which the server will use.`
|
||||
// It is implemented in this way so that downstreams may specify the type of listener
|
||||
// they want to provide Gitea on by default, such as with a hidden service or a p2p network
|
||||
// No need to worry about "breaking" if there would be a refactoring for the Listeners. No compatibility-guarantee for this mechanism
|
||||
var GetListener = DefaultGetListener
|
||||
|
||||
func init() {
|
||||
DefaultMaxHeaderBytes = 0 // use http.DefaultMaxHeaderBytes - which currently is 1 << 20 (1MB)
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"github.com/alecthomas/chroma/v2/formatters/html"
|
||||
"github.com/alecthomas/chroma/v2/lexers"
|
||||
"github.com/alecthomas/chroma/v2/styles"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
lru "github.com/hashicorp/golang-lru/v2"
|
||||
)
|
||||
|
||||
// don't index files larger than this many bytes for performance purposes
|
||||
@@ -35,7 +35,7 @@ var (
|
||||
|
||||
once sync.Once
|
||||
|
||||
cache *lru.TwoQueueCache
|
||||
cache *lru.TwoQueueCache[string, any]
|
||||
|
||||
githubStyles = styles.Get("github")
|
||||
)
|
||||
@@ -46,7 +46,7 @@ func NewContext() {
|
||||
highlightMapping = setting.GetHighlightMapping()
|
||||
|
||||
// The size 512 is simply a conservative rule of thumb
|
||||
c, err := lru.New2Q(512)
|
||||
c, err := lru.New2Q[string, any](512)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to initialize LRU cache for highlighter: %s", err))
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"code.gitea.io/gitea/modules/util/rotatingfilewriter"
|
||||
)
|
||||
|
||||
@@ -19,7 +21,7 @@ type WriterFileOption struct {
|
||||
|
||||
type eventWriterFile struct {
|
||||
*EventWriterBaseImpl
|
||||
fileWriter *rotatingfilewriter.RotatingFileWriter
|
||||
fileWriter io.WriteCloser
|
||||
}
|
||||
|
||||
var _ EventWriter = (*eventWriterFile)(nil)
|
||||
@@ -37,7 +39,10 @@ func NewEventWriterFile(name string, mode WriterMode) EventWriter {
|
||||
CompressionLevel: opt.CompressionLevel,
|
||||
})
|
||||
if err != nil {
|
||||
// if the log file can't be opened, what should it do? panic/exit? ignore logs? fallback to stderr?
|
||||
// it seems that "fallback to stderr" is slightly better than others ....
|
||||
FallbackErrorf("unable to open log file %q: %v", opt.FileName, err)
|
||||
w.fileWriter = nopCloser{Writer: LoggerToWriter(FallbackErrorf)}
|
||||
}
|
||||
w.OutputWriteCloser = w.fileWriter
|
||||
return w
|
||||
|
||||
@@ -51,7 +51,7 @@ func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule {
|
||||
// Render renders orgmode rawbytes to HTML
|
||||
func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
|
||||
htmlWriter := org.NewHTMLWriter()
|
||||
htmlWriter.HighlightCodeBlock = func(source, lang string, inline bool) string {
|
||||
htmlWriter.HighlightCodeBlock = func(source, lang string, inline bool, params map[string]string) string {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Error("Panic in HighlightCodeBlock: %v\n%s", err, log.Stack(2))
|
||||
|
||||
@@ -6,6 +6,7 @@ package markup
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"sync"
|
||||
|
||||
@@ -79,6 +80,14 @@ func createDefaultPolicy() *bluemonday.Policy {
|
||||
policy.AllowURLSchemes(setting.Markdown.CustomURLSchemes...)
|
||||
} else {
|
||||
policy.AllowURLSchemesMatching(allowAllRegex)
|
||||
|
||||
// Even if every scheme is allowed, these three are blocked for security reasons
|
||||
disallowScheme := func(*url.URL) bool {
|
||||
return false
|
||||
}
|
||||
policy.AllowURLSchemeWithCustomPolicy("javascript", disallowScheme)
|
||||
policy.AllowURLSchemeWithCustomPolicy("vbscript", disallowScheme)
|
||||
policy.AllowURLSchemeWithCustomPolicy("data", disallowScheme)
|
||||
}
|
||||
|
||||
// Allow classes for anchors
|
||||
|
||||
@@ -54,8 +54,13 @@ func Test_Sanitizer(t *testing.T) {
|
||||
`<code style="bad-color: red">Hello World</code>`, `<code>Hello World</code>`,
|
||||
|
||||
// URLs
|
||||
`[my custom URL scheme](cbthunderlink://somebase64string)`, `[my custom URL scheme](cbthunderlink://somebase64string)`,
|
||||
`[my custom URL scheme](matrix:roomid/psumPMeAfzgAeQpXMG:feneas.org?action=join)`, `[my custom URL scheme](matrix:roomid/psumPMeAfzgAeQpXMG:feneas.org?action=join)`,
|
||||
`<a href="cbthunderlink://somebase64string)">my custom URL scheme</a>`, `<a href="cbthunderlink://somebase64string)" rel="nofollow">my custom URL scheme</a>`,
|
||||
`<a href="matrix:roomid/psumPMeAfzgAeQpXMG:feneas.org?action=join">my custom URL scheme</a>`, `<a href="matrix:roomid/psumPMeAfzgAeQpXMG:feneas.org?action=join" rel="nofollow">my custom URL scheme</a>`,
|
||||
|
||||
// Disallow dangerous url schemes
|
||||
`<a href="javascript:alert('xss')">bad</a>`, `bad`,
|
||||
`<a href="vbscript:no">bad</a>`, `bad`,
|
||||
`<a href="data:1234">bad</a>`, `bad`,
|
||||
}
|
||||
|
||||
for i := 0; i < len(testCases); i += 2 {
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"io"
|
||||
|
||||
"code.gitea.io/gitea/modules/validation"
|
||||
|
||||
"golang.org/x/net/html/charset"
|
||||
)
|
||||
|
||||
// Metadata represents the metadata of a Maven package
|
||||
@@ -52,7 +54,10 @@ type pomStruct struct {
|
||||
// ParsePackageMetaData parses the metadata of a pom file
|
||||
func ParsePackageMetaData(r io.Reader) (*Metadata, error) {
|
||||
var pom pomStruct
|
||||
if err := xml.NewDecoder(r).Decode(&pom); err != nil {
|
||||
|
||||
dec := xml.NewDecoder(r)
|
||||
dec.CharsetReader = charset.NewReaderLabel
|
||||
if err := dec.Decode(&pom); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/text/encoding/charmap"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -69,4 +70,20 @@ func TestParsePackageMetaData(t *testing.T) {
|
||||
assert.Equal(t, dependencyArtifactID, m.Dependencies[0].ArtifactID)
|
||||
assert.Equal(t, dependencyVersion, m.Dependencies[0].Version)
|
||||
})
|
||||
|
||||
t.Run("Encoding", func(t *testing.T) {
|
||||
// UTF-8 is default but the metadata could be encoded differently
|
||||
pomContent8859_1, err := charmap.ISO8859_1.NewEncoder().String(
|
||||
strings.ReplaceAll(
|
||||
pomContent,
|
||||
`<?xml version="1.0"?>`,
|
||||
`<?xml version="1.0" encoding="ISO-8859-1"?>`,
|
||||
),
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
|
||||
m, err := ParsePackageMetaData(strings.NewReader(pomContent8859_1))
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, m)
|
||||
})
|
||||
}
|
||||
|
||||
+10
-22
@@ -28,27 +28,15 @@ func AssetFS() *assetfs.LayeredFS {
|
||||
return assetfs.Layered(CustomAssets(), BuiltinAssets())
|
||||
}
|
||||
|
||||
// AssetsHandlerFunc implements the static handler for serving custom or original assets.
|
||||
func AssetsHandlerFunc(prefix string) http.HandlerFunc {
|
||||
// FileHandlerFunc implements the static handler for serving files in "public" assets
|
||||
func FileHandlerFunc() http.HandlerFunc {
|
||||
assetFS := AssetFS()
|
||||
prefix = strings.TrimSuffix(prefix, "/") + "/"
|
||||
return func(resp http.ResponseWriter, req *http.Request) {
|
||||
subPath := req.URL.Path
|
||||
if !strings.HasPrefix(subPath, prefix) {
|
||||
return
|
||||
}
|
||||
subPath = strings.TrimPrefix(subPath, prefix)
|
||||
|
||||
if req.Method != "GET" && req.Method != "HEAD" {
|
||||
resp.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if handleRequest(resp, req, assetFS, subPath) {
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteHeader(http.StatusNotFound)
|
||||
handleRequest(resp, req, assetFS, req.URL.Path)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,16 +59,17 @@ func setWellKnownContentType(w http.ResponseWriter, file string) {
|
||||
}
|
||||
}
|
||||
|
||||
func handleRequest(w http.ResponseWriter, req *http.Request, fs http.FileSystem, file string) bool {
|
||||
func handleRequest(w http.ResponseWriter, req *http.Request, fs http.FileSystem, file string) {
|
||||
// actually, fs (http.FileSystem) is designed to be a safe interface, relative paths won't bypass its parent directory, it's also fine to do a clean here
|
||||
f, err := fs.Open(util.PathJoinRelX(file))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
log.Error("[Static] Open %q failed: %v", file, err)
|
||||
return true
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
@@ -88,17 +77,16 @@ func handleRequest(w http.ResponseWriter, req *http.Request, fs http.FileSystem,
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
log.Error("[Static] %q exists, but fails to open: %v", file, err)
|
||||
return true
|
||||
return
|
||||
}
|
||||
|
||||
// Try to serve index file
|
||||
// need to serve index file? (no at the moment)
|
||||
if fi.IsDir() {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return true
|
||||
return
|
||||
}
|
||||
|
||||
serveContent(w, req, fi, fi.ModTime(), f)
|
||||
return true
|
||||
}
|
||||
|
||||
type GzipBytesProvider interface {
|
||||
|
||||
@@ -8,14 +8,14 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
lru "github.com/hashicorp/golang-lru/v2"
|
||||
)
|
||||
|
||||
var lruCache *lru.Cache
|
||||
var lruCache *lru.Cache[string, any]
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
lruCache, err = lru.New(1000)
|
||||
lruCache, err = lru.New[string, any](1000)
|
||||
if err != nil {
|
||||
log.Fatal("failed to new LRU cache, err: %v", err)
|
||||
}
|
||||
|
||||
@@ -303,21 +303,23 @@ func GenerateGitContent(ctx context.Context, templateRepo, generateRepo *repo_mo
|
||||
|
||||
// GenerateRepoOptions contains the template units to generate
|
||||
type GenerateRepoOptions struct {
|
||||
Name string
|
||||
DefaultBranch string
|
||||
Description string
|
||||
Private bool
|
||||
GitContent bool
|
||||
Topics bool
|
||||
GitHooks bool
|
||||
Webhooks bool
|
||||
Avatar bool
|
||||
IssueLabels bool
|
||||
Name string
|
||||
DefaultBranch string
|
||||
Description string
|
||||
Private bool
|
||||
GitContent bool
|
||||
Topics bool
|
||||
GitHooks bool
|
||||
Webhooks bool
|
||||
Avatar bool
|
||||
IssueLabels bool
|
||||
ProtectedBranch bool
|
||||
}
|
||||
|
||||
// IsValid checks whether at least one option is chosen for generation
|
||||
func (gro GenerateRepoOptions) IsValid() bool {
|
||||
return gro.GitContent || gro.Topics || gro.GitHooks || gro.Webhooks || gro.Avatar || gro.IssueLabels // or other items as they are added
|
||||
return gro.GitContent || gro.Topics || gro.GitHooks || gro.Webhooks || gro.Avatar ||
|
||||
gro.IssueLabels || gro.ProtectedBranch // or other items as they are added
|
||||
}
|
||||
|
||||
// GenerateRepository generates a repository from a template
|
||||
|
||||
@@ -86,7 +86,7 @@ func decodeEnvSectionKey(encoded string) (ok bool, section, key string) {
|
||||
key += remaining
|
||||
}
|
||||
section = strings.ToLower(section)
|
||||
ok = section != "" && key != ""
|
||||
ok = key != ""
|
||||
if !ok {
|
||||
section = ""
|
||||
key = ""
|
||||
|
||||
@@ -48,6 +48,12 @@ func TestDecodeEnvironmentKey(t *testing.T) {
|
||||
assert.Equal(t, "", key)
|
||||
assert.False(t, file)
|
||||
|
||||
ok, section, key, file = decodeEnvironmentKey(prefix, suffix, "GITEA____KEY")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "", section)
|
||||
assert.Equal(t, "KEY", key)
|
||||
assert.False(t, file)
|
||||
|
||||
ok, section, key, file = decodeEnvironmentKey(prefix, suffix, "GITEA__SEC__KEY")
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "sec", section)
|
||||
|
||||
@@ -324,7 +324,7 @@ func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, n
|
||||
|
||||
func deprecatedSettingFatal(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey, version string) {
|
||||
if rootCfg.Section(oldSection).HasKey(oldKey) {
|
||||
log.Fatal("Deprecated fallback `[%s]` `%s` present. Use `[%s]` `%s` instead. This fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version)
|
||||
log.Fatal("Deprecated fallback `[%s]` `%s` present. Use `[%s]` `%s` instead. This fallback will be/has been removed in %s. Shutting down", oldSection, oldKey, newSection, newKey, version)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,8 +74,10 @@ func loadPackagesFrom(rootCfg ConfigProvider) (err error) {
|
||||
Packages.ChunkedUploadPath = filepath.ToSlash(filepath.Join(AppDataPath, Packages.ChunkedUploadPath))
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(Packages.ChunkedUploadPath, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("unable to create chunked upload directory: %s (%v)", Packages.ChunkedUploadPath, err)
|
||||
if HasInstallLock(rootCfg) {
|
||||
if err := os.MkdirAll(Packages.ChunkedUploadPath, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("unable to create chunked upload directory: %s (%v)", Packages.ChunkedUploadPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
Packages.LimitTotalOwnerSize = mustBytes(sec, "LIMIT_TOTAL_OWNER_SIZE")
|
||||
|
||||
@@ -17,7 +17,7 @@ var (
|
||||
// AppPath represents the path to the gitea binary
|
||||
AppPath string
|
||||
|
||||
// AppWorkPath is the "working directory" of Gitea. It maps to the environment variable GITEA_WORK_DIR.
|
||||
// AppWorkPath is the "working directory" of Gitea. It maps to the: WORK_PATH in app.ini, "--work-path" flag, environment variable GITEA_WORK_DIR.
|
||||
// If that is not set it is the default set here by the linker or failing that the directory of AppPath.
|
||||
// It is used as the base path for several other paths.
|
||||
AppWorkPath string
|
||||
|
||||
@@ -349,9 +349,4 @@ func loadServerFrom(rootCfg ConfigProvider) {
|
||||
default:
|
||||
LandingPageURL = LandingPage(landingPage)
|
||||
}
|
||||
|
||||
HasRobotsTxt, err = util.IsFile(path.Join(CustomPath, "robots.txt"))
|
||||
if err != nil {
|
||||
log.Error("Unable to check if %s is a file. Error: %v", path.Join(CustomPath, "robots.txt"), err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,26 +16,17 @@ const (
|
||||
CommitStatusError CommitStatusState = "error"
|
||||
// CommitStatusFailure is for when the CommitStatus is Failure
|
||||
CommitStatusFailure CommitStatusState = "failure"
|
||||
// CommitStatusWarning is for when the CommitStatus is Warning
|
||||
CommitStatusWarning CommitStatusState = "warning"
|
||||
// CommitStatusRunning is for when the CommitStatus is Running
|
||||
CommitStatusRunning CommitStatusState = "running"
|
||||
)
|
||||
|
||||
// NoBetterThan returns true if this State is no better than the given State
|
||||
func (css CommitStatusState) NoBetterThan(css2 CommitStatusState) bool {
|
||||
switch css {
|
||||
case CommitStatusError:
|
||||
return true
|
||||
case CommitStatusFailure:
|
||||
return css2 != CommitStatusError
|
||||
case CommitStatusWarning:
|
||||
return css2 != CommitStatusError && css2 != CommitStatusFailure
|
||||
case CommitStatusPending:
|
||||
return css2 != CommitStatusError && css2 != CommitStatusFailure && css2 != CommitStatusWarning
|
||||
default:
|
||||
return css2 != CommitStatusError && css2 != CommitStatusFailure && css2 != CommitStatusWarning && css2 != CommitStatusPending
|
||||
commitStatusPriorities := map[CommitStatusState]int{
|
||||
CommitStatusError: 0,
|
||||
CommitStatusFailure: 1,
|
||||
CommitStatusPending: 2,
|
||||
CommitStatusSuccess: 3,
|
||||
}
|
||||
return commitStatusPriorities[css] <= commitStatusPriorities[css2]
|
||||
}
|
||||
|
||||
// IsPending represents if commit status state is pending
|
||||
@@ -57,8 +48,3 @@ func (css CommitStatusState) IsError() bool {
|
||||
func (css CommitStatusState) IsFailure() bool {
|
||||
return css == CommitStatusFailure
|
||||
}
|
||||
|
||||
// IsWarning represents if commit status state is warning
|
||||
func (css CommitStatusState) IsWarning() bool {
|
||||
return css == CommitStatusWarning
|
||||
}
|
||||
|
||||
@@ -242,6 +242,8 @@ type GenerateRepoOption struct {
|
||||
Avatar bool `json:"avatar"`
|
||||
// include labels in template repo
|
||||
Labels bool `json:"labels"`
|
||||
// include protected branches in template repo
|
||||
ProtectedBranch bool `json:"protected_branch"`
|
||||
}
|
||||
|
||||
// CreateBranchRepoOption options when creating a branch in a repository
|
||||
@@ -331,7 +333,7 @@ type MigrateRepoOptions struct {
|
||||
// required: true
|
||||
RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"`
|
||||
|
||||
// enum: git,github,gitea,gitlab
|
||||
// enum: git,github,gitea,gitlab,gogs,onedev,gitbucket,codebase
|
||||
Service string `json:"service"`
|
||||
AuthUsername string `json:"auth_username"`
|
||||
AuthPassword string `json:"auth_password"`
|
||||
|
||||
@@ -69,4 +69,5 @@ type CommitDateOptions struct {
|
||||
// CommitAffectedFiles store information about files affected by the commit
|
||||
type CommitAffectedFiles struct {
|
||||
Filename string `json:"filename"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ type CreateFileOptions struct {
|
||||
FileOptions
|
||||
// content must be base64 encoded
|
||||
// required: true
|
||||
Content string `json:"content"`
|
||||
ContentBase64 string `json:"content"`
|
||||
}
|
||||
|
||||
// Branch returns branch name
|
||||
@@ -54,7 +54,7 @@ type UpdateFileOptions struct {
|
||||
DeleteFileOptions
|
||||
// content must be base64 encoded
|
||||
// required: true
|
||||
Content string `json:"content"`
|
||||
ContentBase64 string `json:"content"`
|
||||
// from_path (optional) is the path of the original file which will be moved/renamed to the path in the URL
|
||||
FromPath string `json:"from_path" binding:"MaxSize(500)"`
|
||||
}
|
||||
@@ -74,7 +74,7 @@ type ChangeFileOperation struct {
|
||||
// required: true
|
||||
Path string `json:"path" binding:"Required;MaxSize(500)"`
|
||||
// new or updated file content, must be base64 encoded
|
||||
Content string `json:"content"`
|
||||
ContentBase64 string `json:"content"`
|
||||
// sha is the SHA for the file that already exists, required for update or delete
|
||||
SHA string `json:"sha"`
|
||||
// old path of the file to move
|
||||
|
||||
+2
-2
@@ -27,7 +27,7 @@ const defaultSize = 16
|
||||
|
||||
// Init discovers SVGs and populates the `SVGs` variable
|
||||
func Init() error {
|
||||
files, err := public.AssetFS().ListFiles("img/svg")
|
||||
files, err := public.AssetFS().ListFiles("assets/img/svg")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -38,7 +38,7 @@ func Init() error {
|
||||
if path.Ext(file) != ".svg" {
|
||||
continue
|
||||
}
|
||||
bs, err := public.AssetFS().ReadFile("img/svg", file)
|
||||
bs, err := public.AssetFS().ReadFile("assets/img/svg", file)
|
||||
if err != nil {
|
||||
log.Error("Failed to read SVG file %s: %v", file, err)
|
||||
} else {
|
||||
|
||||
@@ -5,17 +5,6 @@ package middleware
|
||||
|
||||
import "net/url"
|
||||
|
||||
// flashes enumerates all the flash types
|
||||
const (
|
||||
SuccessFlash = "SuccessMsg"
|
||||
ErrorFlash = "ErrorMsg"
|
||||
WarnFlash = "WarningMsg"
|
||||
InfoFlash = "InfoMsg"
|
||||
)
|
||||
|
||||
// FlashNow FIXME:
|
||||
var FlashNow bool
|
||||
|
||||
// Flash represents a one time data transfer between two requests.
|
||||
type Flash struct {
|
||||
DataStore ContextDataStore
|
||||
@@ -27,15 +16,12 @@ func (f *Flash) set(name, msg string, current ...bool) {
|
||||
if f.Values == nil {
|
||||
f.Values = make(map[string][]string)
|
||||
}
|
||||
isShow := false
|
||||
if (len(current) == 0 && FlashNow) ||
|
||||
(len(current) > 0 && current[0]) {
|
||||
isShow = true
|
||||
}
|
||||
|
||||
if isShow {
|
||||
showInCurrentPage := len(current) > 0 && current[0]
|
||||
if showInCurrentPage {
|
||||
// assign it to the context data, then the template can use ".Flash.XxxMsg" to render the message
|
||||
f.DataStore.GetData()["Flash"] = f
|
||||
} else {
|
||||
// the message map will be saved into the cookie and be shown in next response (a new page response which decodes the cookie)
|
||||
f.Set(name, msg)
|
||||
}
|
||||
}
|
||||
|
||||
+9
-14
@@ -101,7 +101,7 @@ func (r *Route) wrapMiddlewareAndHandler(h []any) ([]func(http.Handler) http.Han
|
||||
return middlewares, handlerFunc
|
||||
}
|
||||
|
||||
func (r *Route) Methods(method, pattern string, h []any) {
|
||||
func (r *Route) Methods(method, pattern string, h ...any) {
|
||||
middlewares, handlerFunc := r.wrapMiddlewareAndHandler(h)
|
||||
fullPattern := r.getPattern(pattern)
|
||||
if strings.Contains(method, ",") {
|
||||
@@ -126,49 +126,44 @@ func (r *Route) Any(pattern string, h ...any) {
|
||||
r.R.With(middlewares...).HandleFunc(r.getPattern(pattern), handlerFunc)
|
||||
}
|
||||
|
||||
// RouteMethods delegate special methods, it is an alias of "Methods", while the "pattern" is the first parameter
|
||||
func (r *Route) RouteMethods(pattern, methods string, h ...any) {
|
||||
r.Methods(methods, pattern, h)
|
||||
}
|
||||
|
||||
// Delete delegate delete method
|
||||
func (r *Route) Delete(pattern string, h ...any) {
|
||||
r.Methods("DELETE", pattern, h)
|
||||
r.Methods("DELETE", pattern, h...)
|
||||
}
|
||||
|
||||
// Get delegate get method
|
||||
func (r *Route) Get(pattern string, h ...any) {
|
||||
r.Methods("GET", pattern, h)
|
||||
r.Methods("GET", pattern, h...)
|
||||
}
|
||||
|
||||
// GetOptions delegate get and options method
|
||||
func (r *Route) GetOptions(pattern string, h ...any) {
|
||||
r.Methods("GET,OPTIONS", pattern, h)
|
||||
r.Methods("GET,OPTIONS", pattern, h...)
|
||||
}
|
||||
|
||||
// PostOptions delegate post and options method
|
||||
func (r *Route) PostOptions(pattern string, h ...any) {
|
||||
r.Methods("POST,OPTIONS", pattern, h)
|
||||
r.Methods("POST,OPTIONS", pattern, h...)
|
||||
}
|
||||
|
||||
// Head delegate head method
|
||||
func (r *Route) Head(pattern string, h ...any) {
|
||||
r.Methods("HEAD", pattern, h)
|
||||
r.Methods("HEAD", pattern, h...)
|
||||
}
|
||||
|
||||
// Post delegate post method
|
||||
func (r *Route) Post(pattern string, h ...any) {
|
||||
r.Methods("POST", pattern, h)
|
||||
r.Methods("POST", pattern, h...)
|
||||
}
|
||||
|
||||
// Put delegate put method
|
||||
func (r *Route) Put(pattern string, h ...any) {
|
||||
r.Methods("PUT", pattern, h)
|
||||
r.Methods("PUT", pattern, h...)
|
||||
}
|
||||
|
||||
// Patch delegate patch method
|
||||
func (r *Route) Patch(pattern string, h ...any) {
|
||||
r.Methods("PATCH", pattern, h)
|
||||
r.Methods("PATCH", pattern, h...)
|
||||
}
|
||||
|
||||
// ServeHTTP implements http.Handler
|
||||
|
||||
Reference in New Issue
Block a user