From 4875bb3b2b4d9f2b1591b7b3b09d7517180fa7d4 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 1 Sep 2026 07:31:17 +0800 Subject: [PATCH] fix: correct repo/attatchment absolute url and release layout (#39178) --- models/repo/attachment.go | 8 +++-- models/repo/attachment_test.go | 2 +- models/repo/release.go | 2 +- routers/api/packages/composer/api.go | 2 +- routers/api/v1/repo/issue_attachment.go | 6 ++-- .../api/v1/repo/issue_comment_attachment.go | 8 ++--- routers/api/v1/repo/release_attachment.go | 6 ++-- routers/web/repo/issue.go | 2 +- routers/web/repo/issue_comment.go | 2 +- routers/web/repo/setting/webhook.go | 2 +- services/convert/attachment.go | 30 ++++++++++--------- services/convert/issue.go | 4 +-- services/convert/issue_comment.go | 2 +- services/convert/release.go | 2 +- services/repository/files/content.go | 28 +++++------------ services/repository/files/file.go | 4 +-- templates/repo/issue/card.tmpl | 4 +-- .../repo/issue/view_content/attachments.tmpl | 16 +++++----- templates/repo/release/list.tmpl | 2 +- templates/repo/release_tag_header.tmpl | 18 +++++------ .../api_comment_attachment_test.go | 2 +- 21 files changed, 72 insertions(+), 80 deletions(-) diff --git a/models/repo/attachment.go b/models/repo/attachment.go index cb74f310d47..da522d919fb 100644 --- a/models/repo/attachment.go +++ b/models/repo/attachment.go @@ -12,6 +12,7 @@ import ( "path" "gitea.dev/models/db" + "gitea.dev/modules/httplib" "gitea.dev/modules/log" "gitea.dev/modules/setting" "gitea.dev/modules/storage" @@ -62,12 +63,13 @@ func (a *Attachment) RelativePath() string { } // DownloadURL returns the download url of the attached file -func (a *Attachment) DownloadURL() string { +func (a *Attachment) DownloadURL(optCtx ...context.Context) string { + // mail template doesn't have context, so we need to use a default one + ctx := util.OptionalArg(optCtx, context.TODO()) if a.CustomDownloadURL != "" { return a.CustomDownloadURL } - - return setting.AppURL + "attachments/" + url.PathEscape(a.UUID) + return httplib.MakeAbsoluteURL(ctx, setting.AppSubURL+"/attachments/"+url.PathEscape(a.UUID)) } // ErrAttachmentNotExist represents a "AttachmentNotExist" kind of error. diff --git a/models/repo/attachment_test.go b/models/repo/attachment_test.go index a9d76318561..1ea3026010a 100644 --- a/models/repo/attachment_test.go +++ b/models/repo/attachment_test.go @@ -74,7 +74,7 @@ func TestAttachment_DownloadURL(t *testing.T) { UUID: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", ID: 1, } - assert.Equal(t, "https://try.gitea.io/attachments/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attach.DownloadURL()) + assert.Equal(t, "https://try.gitea.io/attachments/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attach.DownloadURL(t.Context())) } func TestUpdateAttachment(t *testing.T) { diff --git a/models/repo/release.go b/models/repo/release.go index 9dd1ab395d7..87b59f8f202 100644 --- a/models/repo/release.go +++ b/models/repo/release.go @@ -433,7 +433,7 @@ func GetReleaseAttachments(ctx context.Context, rels ...*Release) (err error) { // If the names unique, use the URL with the Name instead of the UUID if !hasDuplicateName(release.Attachments) { for _, attachment := range release.Attachments { - attachment.CustomDownloadURL = release.Repo.HTMLURL() + "/releases/download/" + url.PathEscape(release.TagName) + "/" + url.PathEscape(attachment.Name) + attachment.CustomDownloadURL = release.Repo.HTMLURL(ctx) + "/releases/download/" + url.PathEscape(release.TagName) + "/" + url.PathEscape(attachment.Name) } } } diff --git a/routers/api/packages/composer/api.go b/routers/api/packages/composer/api.go index 1e8aa0ae2f3..af861a492ad 100644 --- a/routers/api/packages/composer/api.go +++ b/routers/api/packages/composer/api.go @@ -124,7 +124,7 @@ func createPackageMetadataResponse(ctx *context.Context, registryURL string, pds log.Error("GetDoerRepoPermission[%d]: %v", pd.Repository.ID, err) } else if permission.HasAnyUnitAccessOrPublicAccess() { pkg.Source = Source{ - URL: pd.Repository.HTMLURL(), + URL: pd.Repository.HTMLURL(ctx), Type: "git", Reference: pd.Version.Version, } diff --git a/routers/api/v1/repo/issue_attachment.go b/routers/api/v1/repo/issue_attachment.go index 77f5898e3f0..be1389d6e5f 100644 --- a/routers/api/v1/repo/issue_attachment.go +++ b/routers/api/v1/repo/issue_attachment.go @@ -67,7 +67,7 @@ func GetIssueAttachment(ctx *context.APIContext) { return } - ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) + ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx, ctx.Repo.Repository, attach)) } // ListIssueAttachments lists all attachments of the issue @@ -210,7 +210,7 @@ func CreateIssueAttachment(ctx *context.APIContext) { return } - ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attachment)) + ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx, ctx.Repo.Repository, attachment)) } // EditIssueAttachment updates the given attachment @@ -279,7 +279,7 @@ func EditIssueAttachment(ctx *context.APIContext) { return } - ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attachment)) + ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx, ctx.Repo.Repository, attachment)) } // DeleteIssueAttachment delete a given attachment diff --git a/routers/api/v1/repo/issue_comment_attachment.go b/routers/api/v1/repo/issue_comment_attachment.go index d9cd46ff57b..39aa142d68e 100644 --- a/routers/api/v1/repo/issue_comment_attachment.go +++ b/routers/api/v1/repo/issue_comment_attachment.go @@ -72,7 +72,7 @@ func GetIssueCommentAttachment(ctx *context.APIContext) { return } - ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx.Repo.Repository, attachment)) + ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx, ctx.Repo.Repository, attachment)) } // ListIssueCommentAttachments lists all attachments of the comment @@ -114,7 +114,7 @@ func ListIssueCommentAttachments(ctx *context.APIContext) { return } - ctx.JSON(http.StatusOK, convert.ToAPIAttachments(ctx.Repo.Repository, comment.Attachments)) + ctx.JSON(http.StatusOK, convert.ToAPIAttachments(ctx, ctx.Repo.Repository, comment.Attachments)) } // CreateIssueCommentAttachment creates an attachment and saves the given file @@ -225,7 +225,7 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) { return } - ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attachment)) + ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx, ctx.Repo.Repository, attachment)) } // EditIssueCommentAttachment updates the given attachment @@ -291,7 +291,7 @@ func EditIssueCommentAttachment(ctx *context.APIContext) { ctx.APIErrorInternal(err) return } - ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) + ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx, ctx.Repo.Repository, attach)) } // DeleteIssueCommentAttachment delete a given attachment diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go index ea78305748d..4a1a1f48cde 100644 --- a/routers/api/v1/repo/release_attachment.go +++ b/routers/api/v1/repo/release_attachment.go @@ -98,7 +98,7 @@ func GetReleaseAttachment(ctx *context.APIContext) { return } // FIXME Should prove the existence of the given repo, but results in unnecessary database requests - ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) + ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx, ctx.Repo.Repository, attach)) } // ListReleaseAttachments lists all attachments of the release @@ -263,7 +263,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) { return } - ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) + ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx, ctx.Repo.Repository, attach)) } // EditReleaseAttachment updates the given attachment @@ -346,7 +346,7 @@ func EditReleaseAttachment(ctx *context.APIContext) { ctx.APIErrorInternal(err) return } - ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) + ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx, ctx.Repo.Repository, attach)) } // DeleteReleaseAttachment delete a given attachment diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 67e944a3230..b7241ee0b5b 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -581,7 +581,7 @@ func GetIssueAttachments(ctx *context.Context) { } attachments := make([]*api.Attachment, len(issue.Attachments)) for i := 0; i < len(issue.Attachments); i++ { - attachments[i] = convert.ToAttachment(ctx.Repo.Repository, issue.Attachments[i]) + attachments[i] = convert.ToAttachment(ctx, ctx.Repo.Repository, issue.Attachments[i]) } ctx.JSON(http.StatusOK, attachments) } diff --git a/routers/web/repo/issue_comment.go b/routers/web/repo/issue_comment.go index dd7ba66e5c0..df908277546 100644 --- a/routers/web/repo/issue_comment.go +++ b/routers/web/repo/issue_comment.go @@ -444,7 +444,7 @@ func GetCommentAttachments(ctx *context.Context) { return } for i := 0; i < len(comment.Attachments); i++ { - attachments = append(attachments, convert.ToAttachment(ctx.Repo.Repository, comment.Attachments[i])) + attachments = append(attachments, convert.ToAttachment(ctx, ctx.Repo.Repository, comment.Attachments[i])) } ctx.JSON(http.StatusOK, attachments) } diff --git a/routers/web/repo/setting/webhook.go b/routers/web/repo/setting/webhook.go index b54f3227e31..7bd198482c9 100644 --- a/routers/web/repo/setting/webhook.go +++ b/routers/web/repo/setting/webhook.go @@ -679,7 +679,7 @@ func TestWebhook(ctx *context.Context) { apiCommit := &api.PayloadCommit{ ID: commit.ID.String(), Message: commit.MessageUTF8(), - URL: ctx.Repo.Repository.HTMLURL() + "/commit/" + url.PathEscape(commit.ID.String()), + URL: ctx.Repo.Repository.HTMLURL(ctx) + "/commit/" + url.PathEscape(commit.ID.String()), Author: &api.PayloadUser{ Name: commit.Author.Name, Email: commit.Author.Email, diff --git a/services/convert/attachment.go b/services/convert/attachment.go index 5ab1fadd063..a6f46e1823e 100644 --- a/services/convert/attachment.go +++ b/services/convert/attachment.go @@ -4,30 +4,32 @@ package convert import ( + "context" + repo_model "gitea.dev/models/repo" api "gitea.dev/modules/structs" ) -func WebAssetDownloadURL(repo *repo_model.Repository, attach *repo_model.Attachment) string { - return attach.DownloadURL() +func WebAssetDownloadURL(ctx context.Context, repo *repo_model.Repository, attach *repo_model.Attachment) string { + return attach.DownloadURL(ctx) } -func APIAssetDownloadURL(repo *repo_model.Repository, attach *repo_model.Attachment) string { - return attach.DownloadURL() +func APIAssetDownloadURL(ctx context.Context, repo *repo_model.Repository, attach *repo_model.Attachment) string { + return attach.DownloadURL(ctx) } // ToAttachment converts models.Attachment to api.Attachment for API usage -func ToAttachment(repo *repo_model.Repository, a *repo_model.Attachment) *api.Attachment { - return toAttachment(repo, a, WebAssetDownloadURL) +func ToAttachment(ctx context.Context, repo *repo_model.Repository, a *repo_model.Attachment) *api.Attachment { + return toAttachment(ctx, repo, a, WebAssetDownloadURL) } // ToAPIAttachment converts models.Attachment to api.Attachment for API usage -func ToAPIAttachment(repo *repo_model.Repository, a *repo_model.Attachment) *api.Attachment { - return toAttachment(repo, a, APIAssetDownloadURL) +func ToAPIAttachment(ctx context.Context, repo *repo_model.Repository, a *repo_model.Attachment) *api.Attachment { + return toAttachment(ctx, repo, a, APIAssetDownloadURL) } // toAttachment converts models.Attachment to api.Attachment for API usage -func toAttachment(repo *repo_model.Repository, a *repo_model.Attachment, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Attachment { +func toAttachment(ctx context.Context, repo *repo_model.Repository, a *repo_model.Attachment, getDownloadURL func(ctx context.Context, repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Attachment { return &api.Attachment{ ID: a.ID, Name: a.Name, @@ -35,18 +37,18 @@ func toAttachment(repo *repo_model.Repository, a *repo_model.Attachment, getDown DownloadCount: a.DownloadCount, Size: a.Size, UUID: a.UUID, - DownloadURL: getDownloadURL(repo, a), // for web request json and api request json, return different download urls + DownloadURL: getDownloadURL(ctx, repo, a), // for web/api requests, return different download URLs } } -func ToAPIAttachments(repo *repo_model.Repository, attachments []*repo_model.Attachment) []*api.Attachment { - return toAttachments(repo, attachments, APIAssetDownloadURL) +func ToAPIAttachments(ctx context.Context, repo *repo_model.Repository, attachments []*repo_model.Attachment) []*api.Attachment { + return toAttachments(ctx, repo, attachments, APIAssetDownloadURL) } -func toAttachments(repo *repo_model.Repository, attachments []*repo_model.Attachment, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) []*api.Attachment { +func toAttachments(ctx context.Context, repo *repo_model.Repository, attachments []*repo_model.Attachment, getDownloadURL func(ctx context.Context, repo *repo_model.Repository, attach *repo_model.Attachment) string) []*api.Attachment { converted := make([]*api.Attachment, 0, len(attachments)) for _, attachment := range attachments { - converted = append(converted, toAttachment(repo, attachment, getDownloadURL)) + converted = append(converted, toAttachment(ctx, repo, attachment, getDownloadURL)) } return converted } diff --git a/services/convert/issue.go b/services/convert/issue.go index c775ce93773..54ea9f28dc3 100644 --- a/services/convert/issue.go +++ b/services/convert/issue.go @@ -33,7 +33,7 @@ func ToAPIIssue(ctx context.Context, doer *user_model.User, issue *issues_model. return toIssue(ctx, doer, issue, APIAssetDownloadURL) } -func toIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Issue { +func toIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, getDownloadURL func(ctx context.Context, repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Issue { if err := issue.LoadPoster(ctx); err != nil { return &api.Issue{} } @@ -53,7 +53,7 @@ func toIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Iss Poster: ToUser(ctx, issue.Poster, doer), Title: issue.Title, Body: issue.Content, - Attachments: toAttachments(issue.Repo, issue.Attachments, getDownloadURL), + Attachments: toAttachments(ctx, issue.Repo, issue.Attachments, getDownloadURL), Ref: issue.Ref, State: issue.State(), IsLocked: issue.IsLocked, diff --git a/services/convert/issue_comment.go b/services/convert/issue_comment.go index 2f459224f19..31efd1c1167 100644 --- a/services/convert/issue_comment.go +++ b/services/convert/issue_comment.go @@ -23,7 +23,7 @@ func ToAPIComment(ctx context.Context, repo *repo_model.Repository, c *issues_mo IssueURL: c.IssueURL(ctx), PRURL: c.PRURL(ctx), Body: c.Content, - Attachments: ToAPIAttachments(repo, c.Attachments), + Attachments: ToAPIAttachments(ctx, repo, c.Attachments), Created: c.CreatedUnix.AsTime(), Updated: c.UpdatedUnix.AsTime(), } diff --git a/services/convert/release.go b/services/convert/release.go index f3f08ca9b8c..ddd4d6c59bd 100644 --- a/services/convert/release.go +++ b/services/convert/release.go @@ -29,6 +29,6 @@ func ToAPIRelease(ctx context.Context, repo *repo_model.Repository, r *repo_mode CreatedAt: r.CreatedUnix.AsTime(), PublishedAt: util.Iif(r.IsDraft, nil, r.PublishedUnix.AsTimePtr()), Publisher: ToUser(ctx, r.Publisher, nil), - Attachments: ToAPIAttachments(repo, r.Attachments), + Attachments: ToAPIAttachments(ctx, repo, r.Attachments), } } diff --git a/services/repository/files/content.go b/services/repository/files/content.go index 652d5f794e8..b0af47d7840 100644 --- a/services/repository/files/content.go +++ b/services/repository/files/content.go @@ -202,29 +202,17 @@ func getFileContentsByEntryInternal(ctx context.Context, repo *repo_model.Reposi } // Handle links if entry.IsRegular() || entry.IsLink() || entry.IsExecutable() { - downloadURL, err := url.Parse(repo.HTMLURL() + "/raw/" + refCommit.RefName.RefWebLinkPath() + "/" + util.PathEscapeSegments(opts.TreePath)) - if err != nil { - return nil, err - } - downloadURLString := downloadURL.String() - contentsResponse.DownloadURL = &downloadURLString + downloadURL := repo.HTMLURL(ctx) + "/raw/" + refCommit.RefName.RefWebLinkPath() + "/" + util.PathEscapeSegments(opts.TreePath) + contentsResponse.DownloadURL = &downloadURL } if !entry.IsSubModule() { - htmlURL, err := url.Parse(repo.HTMLURL() + "/src/" + refCommit.RefName.RefWebLinkPath() + "/" + util.PathEscapeSegments(opts.TreePath)) - if err != nil { - return nil, err - } - htmlURLString := htmlURL.String() - contentsResponse.HTMLURL = &htmlURLString - contentsResponse.Links.HTMLURL = &htmlURLString + htmlURL := repo.HTMLURL(ctx) + "/src/" + refCommit.RefName.RefWebLinkPath() + "/" + util.PathEscapeSegments(opts.TreePath) + contentsResponse.HTMLURL = &htmlURL + contentsResponse.Links.HTMLURL = &htmlURL - gitURL, err := url.Parse(repo.APIURL() + "/git/blobs/" + url.PathEscape(entry.ID.String())) - if err != nil { - return nil, err - } - gitURLString := gitURL.String() - contentsResponse.GitURL = &gitURLString - contentsResponse.Links.GitURL = &gitURLString + gitURL := repo.APIURL(ctx) + "/git/blobs/" + url.PathEscape(entry.ID.String()) + contentsResponse.GitURL = &gitURL + contentsResponse.Links.GitURL = &gitURL } return contentsResponse, nil diff --git a/services/repository/files/file.go b/services/repository/files/file.go index 7db74dba8b0..77faae33c4a 100644 --- a/services/repository/files/file.go +++ b/services/repository/files/file.go @@ -89,13 +89,13 @@ func GetFileCommitResponse(ctx context.Context, repo *repo_model.Repository, git } } } - commitHTMLURL, _ := url.Parse(repo.HTMLURL() + "/commit/" + url.PathEscape(commit.ID.String())) + commitHTMLURL := repo.HTMLURL(ctx) + "/commit/" + url.PathEscape(commit.ID.String()) fileCommit := &api.FileCommitResponse{ CommitMeta: api.CommitMeta{ SHA: commit.ID.String(), URL: commitURL.String(), }, - HTMLURL: commitHTMLURL.String(), + HTMLURL: commitHTMLURL, Author: &api.CommitUser{ Identity: api.Identity{ Name: commit.Author.Name, diff --git a/templates/repo/issue/card.tmpl b/templates/repo/issue/card.tmpl index 3c3a1dc0478..e549ba50f37 100644 --- a/templates/repo/issue/card.tmpl +++ b/templates/repo/issue/card.tmpl @@ -3,8 +3,8 @@ {{$attachments := index $.Page.issuesAttachmentMap .ID}} {{if $attachments}}
- {{range $attachments}} - {{.Name}} + {{range $att := $attachments}} + {{$att.Name}} {{end}}
{{end}} diff --git a/templates/repo/issue/view_content/attachments.tmpl b/templates/repo/issue/view_content/attachments.tmpl index bbf0f417533..f284b4eac81 100644 --- a/templates/repo/issue/view_content/attachments.tmpl +++ b/templates/repo/issue/view_content/attachments.tmpl @@ -3,23 +3,23 @@
{{end}} {{$hasThumbnails := false}} - {{- range .Attachments -}} + {{- range $att := .Attachments -}}
- {{.Size | FormatByteSize}} + {{$att.Size | FormatByteSize}}
{{end -}} @@ -30,8 +30,8 @@ {{- range .Attachments -}} {{if FilenameIsImage .Name}} {{if not (StringUtils.Contains (StringUtils.ToString $.RenderedContent) .UUID)}} - - {{.Name}} + + {{.Name}} {{end}} {{end}} diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl index 02731b201af..6ccde83d50b 100644 --- a/templates/repo/release/list.tmpl +++ b/templates/repo/release/list.tmpl @@ -80,7 +80,7 @@