Skip to content

Commit

Permalink
Make Release Download URLs predictable (#23891)
Browse files Browse the repository at this point in the history
As promised in #23817, I have this made a PR to make Release Download
URLs predictable. It currently follows the schema
`<repo>/releases/download/<tag>/<filename>`. this already works, but it
is nowhere shown in the UI or the API. The Problem is, that it is
currently possible to have multiple files with the same name (why do we
even allow this) for a release. I had written some Code to check, if a
Release has 2 or more files with the same Name. If yes, it uses the old
`attachments/<uuid>` URlL if no it uses the new fancy URL.

I had also changed `<repo>/releases/download/<tag>/<filename>` to
directly serve the File instead of redirecting, so people who who use
automatic update checker don't end up with the `attachments/<uuid>` URL.

Fixes #10919

---------

Co-authored-by: a1012112796 <[email protected]>
  • Loading branch information
JakobDev and a1012112796 authored Apr 12, 2023
1 parent e03e827 commit 42919cc
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 15 deletions.
27 changes: 16 additions & 11 deletions models/repo/attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,18 @@ import (

// Attachment represent a attachment of issue/comment/release.
type Attachment struct {
ID int64 `xorm:"pk autoincr"`
UUID string `xorm:"uuid UNIQUE"`
RepoID int64 `xorm:"INDEX"` // this should not be zero
IssueID int64 `xorm:"INDEX"` // maybe zero when creating
ReleaseID int64 `xorm:"INDEX"` // maybe zero when creating
UploaderID int64 `xorm:"INDEX DEFAULT 0"` // Notice: will be zero before this column added
CommentID int64
Name string
DownloadCount int64 `xorm:"DEFAULT 0"`
Size int64 `xorm:"DEFAULT 0"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
ID int64 `xorm:"pk autoincr"`
UUID string `xorm:"uuid UNIQUE"`
RepoID int64 `xorm:"INDEX"` // this should not be zero
IssueID int64 `xorm:"INDEX"` // maybe zero when creating
ReleaseID int64 `xorm:"INDEX"` // maybe zero when creating
UploaderID int64 `xorm:"INDEX DEFAULT 0"` // Notice: will be zero before this column added
CommentID int64
Name string
DownloadCount int64 `xorm:"DEFAULT 0"`
Size int64 `xorm:"DEFAULT 0"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
CustomDownloadURL string `xorm:"-"`
}

func init() {
Expand Down Expand Up @@ -57,6 +58,10 @@ func (a *Attachment) RelativePath() string {

// DownloadURL returns the download url of the attached file
func (a *Attachment) DownloadURL() string {
if a.CustomDownloadURL != "" {
return a.CustomDownloadURL
}

return setting.AppURL + "attachments/" + url.PathEscape(a.UUID)
}

Expand Down
29 changes: 29 additions & 0 deletions models/repo/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package repo
import (
"context"
"fmt"
"net/url"
"sort"
"strconv"
"strings"
Expand Down Expand Up @@ -372,6 +373,34 @@ func GetReleaseAttachments(ctx context.Context, rels ...*Release) (err error) {
sortedRels.Rel[currentIndex].Attachments = append(sortedRels.Rel[currentIndex].Attachments, attachment)
}

// Makes URL's predictable
for _, release := range rels {
// If we have no Repo, we don't need to execute this loop
if release.Repo == nil {
continue
}

// Check if there are two or more attachments with the same name
hasDuplicates := false
foundNames := make(map[string]bool)
for _, attachment := range release.Attachments {
_, found := foundNames[attachment.Name]
if found {
hasDuplicates = true
break
} else {
foundNames[attachment.Name] = true
}
}

// If the names unique, use the URL with the Name instead of the UUID
if !hasDuplicates {
for _, attachment := range release.Attachments {
attachment.CustomDownloadURL = release.Repo.HTMLURL() + "/releases/download/" + url.PathEscape(release.TagName) + "/" + url.PathEscape(attachment.Name)
}
}
}

return err
}

Expand Down
11 changes: 8 additions & 3 deletions routers/web/repo/attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ func DeleteAttachment(ctx *context.Context) {
})
}

// GetAttachment serve attachments
func GetAttachment(ctx *context.Context) {
attach, err := repo_model.GetAttachmentByUUID(ctx, ctx.Params(":uuid"))
// GetAttachment serve attachments with the given UUID
func ServeAttachment(ctx *context.Context, uuid string) {
attach, err := repo_model.GetAttachmentByUUID(ctx, uuid)
if err != nil {
if repo_model.IsErrAttachmentNotExist(err) {
ctx.Error(http.StatusNotFound)
Expand Down Expand Up @@ -153,3 +153,8 @@ func GetAttachment(ctx *context.Context) {
return
}
}

// GetAttachment serve attachments
func GetAttachment(ctx *context.Context) {
ServeAttachment(ctx, ctx.Params(":uuid"))
}
6 changes: 6 additions & 0 deletions routers/web/repo/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ func releasesOrTags(ctx *context.Context, isTagList bool) {
return
}

for _, release := range releases {
release.Repo = ctx.Repo.Repository
}

if err = repo_model.GetReleaseAttachments(ctx, releases...); err != nil {
ctx.ServerError("GetReleaseAttachments", err)
return
Expand Down Expand Up @@ -248,6 +252,8 @@ func SingleRelease(ctx *context.Context) {
ctx.Data["Title"] = release.Title
}

release.Repo = ctx.Repo.Repository

err = repo_model.GetReleaseAttachments(ctx, release)
if err != nil {
ctx.ServerError("GetReleaseAttachments", err)
Expand Down
2 changes: 1 addition & 1 deletion routers/web/repo/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ func RedirectDownload(ctx *context.Context) {
return
}
if att != nil {
ctx.Redirect(att.DownloadURL())
ServeAttachment(ctx, att.UUID)
return
}
}
Expand Down

0 comments on commit 42919cc

Please sign in to comment.