Skip to content

Commit 1875a78

Browse files
Implement Avatar Handler
1 parent 843a191 commit 1875a78

File tree

15 files changed

+771
-182
lines changed

15 files changed

+771
-182
lines changed

custom/conf/app.ini.sample

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,3 +792,9 @@ IS_INPUT_FILE = false
792792
ENABLED = false
793793
; If you want to add authorization, specify a token here
794794
TOKEN =
795+
796+
[storage]
797+
; URL of Bucket where files, such as, attachments, avatars, etc. are stored
798+
; If unset, file://<current_directory> is used to maintain backward compatibility
799+
; Consult https://gocloud.dev/howto/blob/ for URL format for various cloud providers
800+
BUCKET_URL =

docs/content/doc/advanced/config-cheat-sheet.en-us.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,8 +507,12 @@ Two special environment variables are passed to the render command:
507507
- `FORMAT`: Time format to diplay on UI. i.e. RFC1123 or 2006-01-02 15:04:05
508508
- `DEFAULT_UI_LOCATION`: Default location of time on the UI, so that we can display correct user's time on UI. i.e. Shanghai/Asia
509509

510+
## Storage (`storage`)
511+
512+
- `BUCKET_URL`: URL of Bucket where files, such as, attachments, avatars, etc. are stored. If unset, file://<current_directory> is used to maintain backward compatibility. Consult https://gocloud.dev/howto/blob/ for URL format for various cloud providers.
513+
510514
## Other (`other`)
511515

512516
- `SHOW_FOOTER_BRANDING`: **false**: Show Gitea branding in the footer.
513517
- `SHOW_FOOTER_VERSION`: **true**: Show Gitea version information in the footer.
514-
- `SHOW_FOOTER_TEMPLATE_LOAD_TIME`: **true**: Show time of template execution in the footer.
518+
- `SHOW_FOOTER_TEMPLATE_LOAD_TIME`: **true**: Show time of template execution in the footer.

docs/content/doc/advanced/config-cheat-sheet.en-us.md.orig

Lines changed: 518 additions & 0 deletions
Large diffs are not rendered by default.

go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ require (
2525
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d // indirect
2626
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect
2727
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 // indirect
28-
github.com/davecgh/go-spew v1.1.1
2928
github.com/denisenkom/go-mssqldb v0.0.0-20190724012636-11b2859924c1
3029
github.com/dgrijalva/jwt-go v3.2.0+incompatible
3130
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 // indirect

models/attachment.go

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"context"
99
"fmt"
1010
"io"
11+
"os"
1112
"path"
1213

1314
"code.gitea.io/gitea/modules/setting"
@@ -72,38 +73,44 @@ func (a *Attachment) DownloadURL() string {
7273
return fmt.Sprintf("%sattachments/%s", setting.AppURL, a.UUID)
7374
}
7475

75-
// uploadAttachmentToBucket uploads attachments to bucket
76-
func (attach *Attachment) UploadAttachmentToBucket(buf []byte, file io.Reader) (*Attachment, error) {
76+
// UploadToBucket uploads attachments to bucket
77+
func (a *Attachment) UploadToBucket(buf []byte, file io.Reader) (*Attachment, error) {
7778
ctx := context.Background()
78-
bucket, err := blob.OpenBucket(ctx, setting.FileStorage.Bucket)
79+
bucket, err := blob.OpenBucket(ctx, setting.FileStorage.BucketURL)
7980
if err != nil {
80-
return nil, fmt.Errorf("Failed to setup bucket: %v", err)
81+
return nil, fmt.Errorf("could not open bucket: %v", err)
8182
}
82-
bucket = blob.PrefixedBucket(bucket, "data/attachments/")
83+
bucket = blob.PrefixedBucket(bucket, setting.AttachmentPath)
84+
defer bucket.Close()
8385

84-
bucketWriter, err := bucket.NewWriter(ctx, attach.LocalPath(), nil)
86+
bw, err := bucket.NewWriter(ctx, a.LocalPath(), nil)
8587
if err != nil {
86-
return nil, fmt.Errorf("Failed to obtain writer: %v", err)
88+
return nil, fmt.Errorf("failed to obtain writer: %v", err)
8789
}
88-
var fileSize int64
89-
if _, err = bucketWriter.Write(buf); err != nil {
90+
91+
if _, err = bw.Write(buf); err != nil {
9092
return nil, fmt.Errorf("error occurred while writing: %v", err)
91-
} else if fileSize, err = io.Copy(bucketWriter, file); err != nil {
93+
} else if _, err = io.Copy(bw, file); err != nil {
9294
return nil, fmt.Errorf("error occurred while copying: %v", err)
9395
}
94-
attach.Size = fileSize
96+
if err = bw.Close(); err != nil {
97+
return nil, fmt.Errorf("failed to close: %v", err)
98+
}
9599

96-
if err = bucketWriter.Close(); err != nil {
97-
return nil, fmt.Errorf("Failed to close: %v", err)
100+
attrs, err := bucket.Attributes(ctx, a.LocalPath())
101+
if err != nil {
102+
return nil, fmt.Errorf("failed to read attributes: %v", err)
98103
}
99-
return attach, nil
104+
a.Size = attrs.Size
105+
106+
return a, nil
100107
}
101108

102109
// NewAttachment creates a new attachment object.
103110
func NewAttachment(attach *Attachment, buf []byte, file io.Reader) (_ *Attachment, err error) {
104111
attach.UUID = gouuid.NewV4().String()
105112

106-
attach, err = attach.UploadAttachmentToBucket(buf, file)
113+
attach, err = attach.UploadToBucket(buf, file)
107114
if err != nil {
108115
return nil, err
109116
}
@@ -194,47 +201,44 @@ func getAttachmentByReleaseIDFileName(e Engine, releaseID int64, fileName string
194201
return attach, nil
195202
}
196203

197-
// GetAttachmentReader provides attachment reader from bucket
198-
func (attach *Attachment) GetAttachmentReader() (io.ReadCloser, error) {
204+
// Open provides attachment reader from bucket
205+
func (a *Attachment) Open() (io.ReadCloser, error) {
199206
ctx := context.Background()
200-
bucket, err := blob.OpenBucket(ctx, setting.FileStorage.Bucket)
207+
bucket, err := blob.OpenBucket(ctx, setting.FileStorage.BucketURL)
201208
if err != nil {
202-
return nil, fmt.Errorf("Failed to setup bucket %v", err)
209+
return nil, fmt.Errorf("could not open bucket %v", err)
203210
}
204-
bucket = blob.PrefixedBucket(bucket, "data/attachments/")
211+
bucket = blob.PrefixedBucket(bucket, setting.AttachmentPath)
212+
defer bucket.Close()
205213

206-
exist, err := bucket.Exists(ctx, attach.LocalPath())
214+
exist, err := bucket.Exists(ctx, a.LocalPath())
207215
if err != nil {
208216
return nil, err
209217
} else if !exist {
210-
return nil, fmt.Errorf("Attachment not found")
211-
}
212-
213-
reader, err := bucket.NewReader(ctx, attach.LocalPath(), nil)
214-
if err != nil {
215-
return nil, err
218+
return nil, os.ErrNotExist
216219
}
217220

218-
return reader, nil
221+
return bucket.NewReader(ctx, a.LocalPath(), nil)
219222
}
220223

221-
// deleteAttachmentFromBucket deletes attachments from bucket
222-
func (attach *Attachment) deleteAttachmentFromBucket() error {
224+
// deleteFromBucket deletes attachments from bucket
225+
func (a *Attachment) deleteFromBucket() error {
223226
ctx := context.Background()
224-
bucket, err := blob.OpenBucket(ctx, setting.FileStorage.Bucket)
227+
bucket, err := blob.OpenBucket(ctx, setting.FileStorage.BucketURL)
225228
if err != nil {
226-
return fmt.Errorf("Failed to setup bucket: %v", err)
229+
return fmt.Errorf("could not open bucket: %v", err)
227230
}
228-
bucket = blob.PrefixedBucket(bucket, "data/attachments/")
231+
bucket = blob.PrefixedBucket(bucket, setting.AttachmentPath)
232+
defer bucket.Close()
229233

230-
exist, err := bucket.Exists(ctx, attach.LocalPath())
234+
exist, err := bucket.Exists(ctx, a.LocalPath())
231235
if err != nil {
232236
return err
233237
} else if !exist {
234-
return fmt.Errorf("repo avatar not found")
238+
return os.ErrNotExist
235239
}
236240

237-
return bucket.Delete(ctx, attach.LocalPath())
241+
return bucket.Delete(ctx, a.LocalPath())
238242
}
239243

240244
// DeleteAttachment deletes the given attachment and optionally the associated file.
@@ -261,7 +265,7 @@ func DeleteAttachments(attachments []*Attachment, remove bool) (int, error) {
261265

262266
if remove {
263267
for i, a := range attachments {
264-
if err := a.deleteAttachmentFromBucket(); err != nil {
268+
if err := a.deleteFromBucket(); err != nil {
265269
return i, err
266270
}
267271
}

models/repo.go

Lines changed: 26 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2544,7 +2544,6 @@ func (repo *Repository) generateRandomAvatar(e Engine) error {
25442544
}
25452545

25462546
repo.Avatar = idToString
2547-
25482547
if err := repo.uploadAvatarToBucket(img); err != nil {
25492548
return err
25502549
}
@@ -2578,55 +2577,23 @@ func (repo *Repository) RelAvatarLink() string {
25782577
return repo.relAvatarLink(x)
25792578
}
25802579

2581-
// getAvatarLinkFromBucket returns repo avatar link from bucket
2582-
func (repo *Repository) getAvatarLinkFromBucket() (string, error) {
2583-
ctx := context.Background()
2584-
2585-
bucket, err := blob.OpenBucket(ctx, setting.FileStorage.Bucket)
2586-
if err != nil {
2587-
return "", fmt.Errorf("Failed to setup bucket: %v", err)
2588-
}
2589-
bucket = blob.PrefixedBucket(bucket, setting.RepositoryAvatarUploadPath+"/")
2590-
2591-
exist, err := bucket.Exists(ctx, repo.Avatar)
2592-
if exist {
2593-
opts := &blob.SignedURLOptions{
2594-
Expiry: blob.DefaultSignedURLExpiry,
2595-
}
2596-
signedUrl, err := bucket.SignedURL(ctx, repo.Avatar, opts)
2597-
if err != nil {
2598-
return "", err
2599-
}
2600-
return signedUrl, nil
2601-
}
2602-
return "", fmt.Errorf("File doesn't exist, error %v", err)
2603-
}
2604-
26052580
func (repo *Repository) relAvatarLink(e Engine) string {
26062581
// If no avatar - path is empty
26072582
avatarPath := repo.CustomAvatarPath()
2608-
2609-
var avatarLink string
2610-
var err error
2611-
2612-
if len(avatarPath) > 0 {
2613-
avatarLink, err = repo.getAvatarLinkFromBucket()
2614-
}
2615-
if len(avatarPath) == 0 || err != nil {
2583+
if len(avatarPath) == 0 || !isAvatarValid(repo.CustomAvatarPath()) {
26162584
switch mode := setting.RepositoryAvatarFallback; mode {
26172585
case "image":
26182586
return setting.RepositoryAvatarFallbackImage
26192587
case "random":
26202588
if err := repo.generateRandomAvatar(e); err != nil {
26212589
log.Error("generateRandomAvatar: %v", err)
26222590
}
2623-
avatarLink, _ = repo.getAvatarLinkFromBucket()
26242591
default:
26252592
// default behaviour: do not display avatar
26262593
return ""
26272594
}
26282595
}
2629-
return avatarLink
2596+
return setting.AppSubURL + "/avatars?obj=" + repo.CustomAvatarPath()
26302597
}
26312598

26322599
// avatarLink returns user avatar absolute link.
@@ -2644,27 +2611,28 @@ func (repo *Repository) avatarLink(e Engine) string {
26442611
// uploadAvatarToBucket uploads repo avatar to bucket
26452612
func (repo *Repository) uploadAvatarToBucket(img image.Image) error {
26462613
ctx := context.Background()
2647-
bucket, err := blob.OpenBucket(ctx, setting.FileStorage.Bucket)
2614+
bucket, err := blob.OpenBucket(ctx, setting.FileStorage.BucketURL)
26482615
if err != nil {
2649-
return fmt.Errorf("failed to setup bucket: %v", err)
2616+
return fmt.Errorf("could not open bucket: %v", err)
26502617
}
2651-
bucket = blob.PrefixedBucket(bucket, setting.RepositoryAvatarUploadPath+"/")
2618+
bucket = blob.PrefixedBucket(bucket, setting.RepositoryAvatarUploadPath)
2619+
defer bucket.Close()
26522620

26532621
buf := new(bytes.Buffer)
26542622
if err = png.Encode(buf, img); err != nil {
26552623
return fmt.Errorf("failed to encode: %v", err)
26562624
}
26572625
imgData := buf.Bytes()
26582626

2659-
bucketWriter, err := bucket.NewWriter(ctx, repo.Avatar, nil)
2627+
bw, err := bucket.NewWriter(ctx, repo.Avatar, nil)
26602628
if err != nil {
26612629
return fmt.Errorf("failed to obtain writer: %v", err)
26622630
}
26632631

2664-
if _, err = bucketWriter.Write(imgData); err != nil {
2665-
return fmt.Errorf("error occurred: %v", err)
2632+
if _, err = bw.Write(imgData); err != nil {
2633+
return fmt.Errorf("failed to write: %v", err)
26662634
}
2667-
if err = bucketWriter.Close(); err != nil {
2635+
if err = bw.Close(); err != nil {
26682636
return fmt.Errorf("failed to close: %v", err)
26692637
}
26702638

@@ -2692,42 +2660,42 @@ func (repo *Repository) UploadAvatar(data []byte) error {
26922660
// Then repo will be removed - only it avatar file will be removed
26932661
newAvatar := fmt.Sprintf("%d-%x", repo.ID, md5.Sum(data))
26942662
repo.Avatar = newAvatar
2695-
if len(oldAvatarPath) > 0 && oldAvatarPath != repo.CustomAvatarPath() {
2696-
repo.Avatar = oldAvatar
2697-
if err := repo.deleteAvatarFromBucket(); err != nil {
2698-
log.Trace("DeleteOldAvatar: ", err)
2699-
}
2700-
}
2701-
repo.Avatar = newAvatar
2702-
27032663
if _, err := sess.ID(repo.ID).Cols("avatar").Update(repo); err != nil {
27042664
return fmt.Errorf("UploadAvatar: Update repository avatar: %v", err)
27052665
}
27062666

2707-
if err := repo.uploadAvatarToBucket(*m); err != nil {
2667+
if err := repo.uploadAvatarToBucket(m); err != nil {
27082668
return err
27092669
}
27102670

2671+
if len(oldAvatarPath) > 0 && oldAvatarPath != repo.CustomAvatarPath() {
2672+
repo.Avatar = oldAvatar
2673+
if err := repo.deleteAvatarFromBucket(); err != nil {
2674+
log.Trace("DeleteOldAvatar: ", err)
2675+
}
2676+
repo.Avatar = newAvatar
2677+
}
2678+
27112679
return sess.Commit()
27122680
}
27132681

27142682
// deleteAvatarFromBucket deletes repo avatar from bucket
27152683
func (repo *Repository) deleteAvatarFromBucket() error {
27162684
ctx := context.Background()
2717-
bucket, err := blob.OpenBucket(ctx, setting.FileStorage.Bucket)
2685+
bucket, err := blob.OpenBucket(ctx, setting.FileStorage.BucketURL)
27182686
if err != nil {
2719-
return fmt.Errorf("Failed to setup bucket: %v", err)
2687+
return fmt.Errorf("could not open bucket: %v", err)
27202688
}
2721-
bucket = blob.PrefixedBucket(bucket, setting.RepositoryAvatarUploadPath+"/")
2689+
bucket = blob.PrefixedBucket(bucket, setting.RepositoryAvatarUploadPath)
2690+
defer bucket.Close()
27222691

27232692
exist, err := bucket.Exists(ctx, repo.Avatar)
27242693
if err != nil {
27252694
return err
2726-
} else if !exist {
2727-
return errors.New("Repo avatar not found")
2695+
} else if exist {
2696+
return bucket.Delete(ctx, repo.Avatar)
27282697
}
2729-
2730-
return bucket.Delete(ctx, repo.Avatar)
2698+
return nil
27312699
}
27322700

27332701
// DeleteAvatar deletes the repos's custom avatar.

0 commit comments

Comments
 (0)