@@ -7,10 +7,12 @@ package models
7
7
8
8
import (
9
9
"bytes"
10
+ "context"
10
11
"crypto/md5"
11
12
"errors"
12
13
"fmt"
13
14
"html/template"
15
+ "image"
14
16
15
17
// Needed for jpeg support
16
18
_ "image/jpeg"
@@ -38,6 +40,7 @@ import (
38
40
39
41
"github.com/Unknwon/com"
40
42
"github.com/go-xorm/xorm"
43
+ "gocloud.dev/blob"
41
44
ini "gopkg.in/ini.v1"
42
45
"xorm.io/builder"
43
46
)
@@ -1934,11 +1937,8 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
1934
1937
}
1935
1938
1936
1939
if len (repo .Avatar ) > 0 {
1937
- avatarPath := repo .CustomAvatarPath ()
1938
- if com .IsExist (avatarPath ) {
1939
- if err := os .Remove (avatarPath ); err != nil {
1940
- return fmt .Errorf ("Failed to remove %s: %v" , avatarPath , err )
1941
- }
1940
+ if err := repo .deleteAvatarFromBucket (); err != nil {
1941
+ return err
1942
1942
}
1943
1943
}
1944
1944
@@ -2544,18 +2544,11 @@ func (repo *Repository) generateRandomAvatar(e Engine) error {
2544
2544
}
2545
2545
2546
2546
repo .Avatar = idToString
2547
- if err = os .MkdirAll (filepath .Dir (repo .CustomAvatarPath ()), os .ModePerm ); err != nil {
2548
- return fmt .Errorf ("MkdirAll: %v" , err )
2549
- }
2550
- fw , err := os .Create (repo .CustomAvatarPath ())
2551
- if err != nil {
2552
- return fmt .Errorf ("Create: %v" , err )
2553
- }
2554
- defer fw .Close ()
2555
2547
2556
- if err = png . Encode ( fw , img ); err != nil {
2557
- return fmt . Errorf ( "Encode: %v" , err )
2548
+ if err := repo . uploadAvatarToBucket ( img ); err != nil {
2549
+ return err
2558
2550
}
2551
+
2559
2552
log .Info ("New random avatar created for repository: %d" , repo .ID )
2560
2553
2561
2554
if _ , err := e .ID (repo .ID ).Cols ("avatar" ).NoAutoTime ().Update (repo ); err != nil {
@@ -2585,23 +2578,55 @@ func (repo *Repository) RelAvatarLink() string {
2585
2578
return repo .relAvatarLink (x )
2586
2579
}
2587
2580
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
+
2588
2605
func (repo * Repository ) relAvatarLink (e Engine ) string {
2589
2606
// If no avatar - path is empty
2590
2607
avatarPath := repo .CustomAvatarPath ()
2591
- if len (avatarPath ) == 0 || ! com .IsFile (avatarPath ) {
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 {
2592
2616
switch mode := setting .RepositoryAvatarFallback ; mode {
2593
2617
case "image" :
2594
2618
return setting .RepositoryAvatarFallbackImage
2595
2619
case "random" :
2596
2620
if err := repo .generateRandomAvatar (e ); err != nil {
2597
2621
log .Error ("generateRandomAvatar: %v" , err )
2598
2622
}
2623
+ avatarLink , _ = repo .getAvatarLinkFromBucket ()
2599
2624
default :
2600
2625
// default behaviour: do not display avatar
2601
2626
return ""
2602
2627
}
2603
2628
}
2604
- return setting . AppSubURL + "/repo-avatars/" + repo . Avatar
2629
+ return avatarLink
2605
2630
}
2606
2631
2607
2632
// avatarLink returns user avatar absolute link.
@@ -2616,6 +2641,36 @@ func (repo *Repository) avatarLink(e Engine) string {
2616
2641
return link
2617
2642
}
2618
2643
2644
+ // uploadAvatarToBucket uploads repo avatar to bucket
2645
+ func (repo * Repository ) uploadAvatarToBucket (img image.Image ) error {
2646
+ ctx := context .Background ()
2647
+ bucket , err := blob .OpenBucket (ctx , setting .FileStorage .Bucket )
2648
+ if err != nil {
2649
+ return fmt .Errorf ("failed to setup bucket: %v" , err )
2650
+ }
2651
+ bucket = blob .PrefixedBucket (bucket , setting .RepositoryAvatarUploadPath + "/" )
2652
+
2653
+ buf := new (bytes.Buffer )
2654
+ if err = png .Encode (buf , img ); err != nil {
2655
+ return fmt .Errorf ("failed to encode: %v" , err )
2656
+ }
2657
+ imgData := buf .Bytes ()
2658
+
2659
+ bucketWriter , err := bucket .NewWriter (ctx , repo .Avatar , nil )
2660
+ if err != nil {
2661
+ return fmt .Errorf ("failed to obtain writer: %v" , err )
2662
+ }
2663
+
2664
+ if _ , err = bucketWriter .Write (imgData ); err != nil {
2665
+ return fmt .Errorf ("error occurred: %v" , err )
2666
+ }
2667
+ if err = bucketWriter .Close (); err != nil {
2668
+ return fmt .Errorf ("failed to close: %v" , err )
2669
+ }
2670
+
2671
+ return nil
2672
+ }
2673
+
2619
2674
// UploadAvatar saves custom avatar for repository.
2620
2675
// FIXME: split uploads to different subdirs in case we have massive number of repos.
2621
2676
func (repo * Repository ) UploadAvatar (data []byte ) error {
@@ -2630,36 +2685,49 @@ func (repo *Repository) UploadAvatar(data []byte) error {
2630
2685
return err
2631
2686
}
2632
2687
2688
+ oldAvatar := repo .Avatar
2633
2689
oldAvatarPath := repo .CustomAvatarPath ()
2634
2690
2635
2691
// Users can upload the same image to other repo - prefix it with ID
2636
2692
// Then repo will be removed - only it avatar file will be removed
2637
- repo .Avatar = fmt .Sprintf ("%d-%x" , repo .ID , md5 .Sum (data ))
2693
+ newAvatar := fmt .Sprintf ("%d-%x" , repo .ID , md5 .Sum (data ))
2694
+ 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
+
2638
2703
if _ , err := sess .ID (repo .ID ).Cols ("avatar" ).Update (repo ); err != nil {
2639
2704
return fmt .Errorf ("UploadAvatar: Update repository avatar: %v" , err )
2640
2705
}
2641
2706
2642
- if err := os . MkdirAll ( setting . RepositoryAvatarUploadPath , os . ModePerm ); err != nil {
2643
- return fmt . Errorf ( "UploadAvatar: Failed to create dir %s: %v" , setting . RepositoryAvatarUploadPath , err )
2707
+ if err := repo . uploadAvatarToBucket ( * m ); err != nil {
2708
+ return err
2644
2709
}
2645
2710
2646
- fw , err := os .Create (repo .CustomAvatarPath ())
2647
- if err != nil {
2648
- return fmt .Errorf ("UploadAvatar: Create file: %v" , err )
2649
- }
2650
- defer fw .Close ()
2711
+ return sess .Commit ()
2712
+ }
2651
2713
2652
- if err = png .Encode (fw , * m ); err != nil {
2653
- return fmt .Errorf ("UploadAvatar: Encode png: %v" , err )
2714
+ // deleteAvatarFromBucket deletes repo avatar from bucket
2715
+ func (repo * Repository ) deleteAvatarFromBucket () error {
2716
+ ctx := context .Background ()
2717
+ bucket , err := blob .OpenBucket (ctx , setting .FileStorage .Bucket )
2718
+ if err != nil {
2719
+ return fmt .Errorf ("Failed to setup bucket: %v" , err )
2654
2720
}
2721
+ bucket = blob .PrefixedBucket (bucket , setting .RepositoryAvatarUploadPath + "/" )
2655
2722
2656
- if len (oldAvatarPath ) > 0 && oldAvatarPath != repo .CustomAvatarPath () {
2657
- if err := os .Remove (oldAvatarPath ); err != nil {
2658
- return fmt .Errorf ("UploadAvatar: Failed to remove old repo avatar %s: %v" , oldAvatarPath , err )
2659
- }
2723
+ exist , err := bucket .Exists (ctx , repo .Avatar )
2724
+ if err != nil {
2725
+ return err
2726
+ } else if ! exist {
2727
+ return errors .New ("Repo avatar not found" )
2660
2728
}
2661
2729
2662
- return sess . Commit ( )
2730
+ return bucket . Delete ( ctx , repo . Avatar )
2663
2731
}
2664
2732
2665
2733
// DeleteAvatar deletes the repos's custom avatar.
@@ -2679,19 +2747,15 @@ func (repo *Repository) DeleteAvatar() error {
2679
2747
return err
2680
2748
}
2681
2749
2750
+ if err := repo .deleteAvatarFromBucket (); err != nil {
2751
+ return err
2752
+ }
2753
+
2682
2754
repo .Avatar = ""
2683
2755
if _ , err := sess .ID (repo .ID ).Cols ("avatar" ).Update (repo ); err != nil {
2684
2756
return fmt .Errorf ("DeleteAvatar: Update repository avatar: %v" , err )
2685
2757
}
2686
2758
2687
- if _ , err := os .Stat (avatarPath ); err == nil {
2688
- if err := os .Remove (avatarPath ); err != nil {
2689
- return fmt .Errorf ("DeleteAvatar: Failed to remove %s: %v" , avatarPath , err )
2690
- }
2691
- } else {
2692
- // // Schrodinger: file may or may not exist. See err for details.
2693
- log .Trace ("DeleteAvatar[%d]: %v" , err )
2694
- }
2695
2759
return sess .Commit ()
2696
2760
}
2697
2761
0 commit comments