mirror of
				https://github.com/cloudreve/cloudreve.git
				synced 2025-11-04 04:47:24 +08:00 
			
		
		
		
	Feat: download file from single file share
This commit is contained in:
		@ -25,10 +25,11 @@ type Group struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// GroupOption 用户组其他配置
 | 
					// GroupOption 用户组其他配置
 | 
				
			||||||
type GroupOption struct {
 | 
					type GroupOption struct {
 | 
				
			||||||
	ArchiveDownloadEnabled bool `json:"archive_download"`
 | 
						ArchiveDownloadEnabled bool `json:"archive_download,omitempty"`
 | 
				
			||||||
	ArchiveTaskEnabled     bool `json:"archive_task"`
 | 
						ArchiveTaskEnabled     bool `json:"archive_task,omitempty"`
 | 
				
			||||||
	OneTimeDownloadEnabled bool `json:"one_time_download"`
 | 
						OneTimeDownloadEnabled bool `json:"one_time_download,omitempty"`
 | 
				
			||||||
	ShareDownloadEnabled   bool `json:"share_download"`
 | 
						ShareDownloadEnabled   bool `json:"share_download,omitempty"`
 | 
				
			||||||
 | 
						ShareFreeEnabled       bool `json:"share_free,omitempty"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetAria2Option 获取用户离线下载设备
 | 
					// GetAria2Option 获取用户离线下载设备
 | 
				
			||||||
 | 
				
			|||||||
@ -108,6 +108,7 @@ solid #e9e9e9;"bgcolor="#fff"><tbody><tr style="font-family: 'Helvetica Neue',He
 | 
				
			|||||||
		{Name: "upload_session_timeout", Value: `86400`, Type: "timeout"},
 | 
							{Name: "upload_session_timeout", Value: `86400`, Type: "timeout"},
 | 
				
			||||||
		{Name: "slave_api_timeout", Value: `60`, Type: "timeout"},
 | 
							{Name: "slave_api_timeout", Value: `60`, Type: "timeout"},
 | 
				
			||||||
		{Name: "onedrive_monitor_timeout", Value: `600`, Type: "timeout"},
 | 
							{Name: "onedrive_monitor_timeout", Value: `600`, Type: "timeout"},
 | 
				
			||||||
 | 
							{Name: "share_download_session_timeout", Value: `2073600`, Type: "timeout"},
 | 
				
			||||||
		{Name: "onedrive_callback_check", Value: `20`, Type: "timeout"},
 | 
							{Name: "onedrive_callback_check", Value: `20`, Type: "timeout"},
 | 
				
			||||||
		{Name: "onedrive_chunk_retries", Value: `1`, Type: "retry"},
 | 
							{Name: "onedrive_chunk_retries", Value: `1`, Type: "retry"},
 | 
				
			||||||
		{Name: "allowdVisitorDownload", Value: `false`, Type: "share"},
 | 
							{Name: "allowdVisitorDownload", Value: `false`, Type: "share"},
 | 
				
			||||||
@ -185,6 +186,7 @@ func addDefaultGroups() {
 | 
				
			|||||||
			OptionsSerialized: GroupOption{
 | 
								OptionsSerialized: GroupOption{
 | 
				
			||||||
				ArchiveDownloadEnabled: true,
 | 
									ArchiveDownloadEnabled: true,
 | 
				
			||||||
				ArchiveTaskEnabled:     true,
 | 
									ArchiveTaskEnabled:     true,
 | 
				
			||||||
 | 
									ShareDownloadEnabled:   true,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if err := DB.Create(&defaultAdminGroup).Error; err != nil {
 | 
							if err := DB.Create(&defaultAdminGroup).Error; err != nil {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,9 @@
 | 
				
			|||||||
package model
 | 
					package model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"github.com/HFO4/cloudreve/pkg/cache"
 | 
				
			||||||
	"github.com/HFO4/cloudreve/pkg/hashid"
 | 
						"github.com/HFO4/cloudreve/pkg/hashid"
 | 
				
			||||||
	"github.com/HFO4/cloudreve/pkg/util"
 | 
						"github.com/HFO4/cloudreve/pkg/util"
 | 
				
			||||||
	"github.com/jinzhu/gorm"
 | 
						"github.com/jinzhu/gorm"
 | 
				
			||||||
@ -86,6 +89,14 @@ func (share *Share) GetCreator() *User {
 | 
				
			|||||||
	return &share.User
 | 
						return &share.User
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetSource 返回源对象
 | 
				
			||||||
 | 
					func (share *Share) GetSource() interface{} {
 | 
				
			||||||
 | 
						if share.IsDir {
 | 
				
			||||||
 | 
							return share.GetSourceFolder()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return share.GetSourceFile()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetSourceFolder 获取源目录
 | 
					// GetSourceFolder 获取源目录
 | 
				
			||||||
func (share *Share) GetSourceFolder() *Folder {
 | 
					func (share *Share) GetSourceFolder() *Folder {
 | 
				
			||||||
	if share.Folder.ID == 0 {
 | 
						if share.Folder.ID == 0 {
 | 
				
			||||||
@ -107,3 +118,69 @@ func (share *Share) GetSourceFile() *File {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return &share.File
 | 
						return &share.File
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CanBeDownloadBy 返回此分享是否可以被给定用户下载
 | 
				
			||||||
 | 
					func (share *Share) CanBeDownloadBy(user *User) error {
 | 
				
			||||||
 | 
						// 用户组权限
 | 
				
			||||||
 | 
						if !user.Group.OptionsSerialized.ShareDownloadEnabled {
 | 
				
			||||||
 | 
							if user.IsAnonymous() {
 | 
				
			||||||
 | 
								return errors.New("未登录用户无法下载")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return errors.New("您当前的用户组无权下载")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 需要积分但未登录
 | 
				
			||||||
 | 
						if share.Score > 0 && user.IsAnonymous() {
 | 
				
			||||||
 | 
							return errors.New("未登录用户无法下载")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WasDownloadedBy 返回分享是否已被用户下载过
 | 
				
			||||||
 | 
					func (share *Share) WasDownloadedBy(user *User) bool {
 | 
				
			||||||
 | 
						_, exist := cache.Get(fmt.Sprintf("share_%d_%d", share.ID, user.ID))
 | 
				
			||||||
 | 
						return exist
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DownloadBy 增加下载次数、检查积分等,匿名用户不会缓存
 | 
				
			||||||
 | 
					func (share *Share) DownloadBy(user *User) error {
 | 
				
			||||||
 | 
						if !share.WasDownloadedBy(user) {
 | 
				
			||||||
 | 
							if err := share.Purchase(user); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							share.Downloaded()
 | 
				
			||||||
 | 
							if !user.IsAnonymous() {
 | 
				
			||||||
 | 
								cache.Set(fmt.Sprintf("share_%d_%d", share.ID, user.ID), true,
 | 
				
			||||||
 | 
									GetIntSetting("share_download_session_timeout", 2073600))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Purchase 使用积分购买分享
 | 
				
			||||||
 | 
					func (share *Share) Purchase(user *User) error {
 | 
				
			||||||
 | 
						// 不需要付积分
 | 
				
			||||||
 | 
						if share.Score == 0 || user.Group.OptionsSerialized.ShareFreeEnabled {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ok := user.PayScore(share.Score)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return errors.New("积分不足")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Viewed 增加访问次数
 | 
				
			||||||
 | 
					func (share *Share) Viewed() {
 | 
				
			||||||
 | 
						share.Views++
 | 
				
			||||||
 | 
						DB.Model(share).UpdateColumn("views", gorm.Expr("views + ?", 1))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Downloaded 增加下载次数
 | 
				
			||||||
 | 
					func (share *Share) Downloaded() {
 | 
				
			||||||
 | 
						share.Downloads++
 | 
				
			||||||
 | 
						DB.Model(share).UpdateColumn("downloads", gorm.Expr("downloads + ?", 1))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -39,6 +39,7 @@ type User struct {
 | 
				
			|||||||
	Avatar        string
 | 
						Avatar        string
 | 
				
			||||||
	Options       string `json:"-",gorm:"size:4096"`
 | 
						Options       string `json:"-",gorm:"size:4096"`
 | 
				
			||||||
	Authn         string `gorm:"size:8192"`
 | 
						Authn         string `gorm:"size:8192"`
 | 
				
			||||||
 | 
						Score         int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 关联模型
 | 
						// 关联模型
 | 
				
			||||||
	Group  Group  `gorm:"association_autoupdate:false"`
 | 
						Group  Group  `gorm:"association_autoupdate:false"`
 | 
				
			||||||
@ -93,6 +94,20 @@ func (user *User) IncreaseStorage(size uint64) bool {
 | 
				
			|||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PayScore 扣除积分,返回是否成功
 | 
				
			||||||
 | 
					// todo 测试
 | 
				
			||||||
 | 
					func (user *User) PayScore(score int) bool {
 | 
				
			||||||
 | 
						if score == 0 {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if score <= user.Score {
 | 
				
			||||||
 | 
							user.Score -= score
 | 
				
			||||||
 | 
							DB.Model(user).UpdateColumn("score", gorm.Expr("score - ?", score))
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IncreaseStorageWithoutCheck 忽略可用容量,增加用户已用容量
 | 
					// IncreaseStorageWithoutCheck 忽略可用容量,增加用户已用容量
 | 
				
			||||||
func (user *User) IncreaseStorageWithoutCheck(size uint64) {
 | 
					func (user *User) IncreaseStorageWithoutCheck(size uint64) {
 | 
				
			||||||
	if size == 0 {
 | 
						if size == 0 {
 | 
				
			||||||
 | 
				
			|||||||
@ -14,7 +14,7 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	ErrAuthFailed = serializer.NewError(serializer.CodeNoRightErr, "鉴权失败", nil)
 | 
						ErrAuthFailed = serializer.NewError(serializer.CodeNoPermissionErr, "鉴权失败", nil)
 | 
				
			||||||
	ErrExpired    = serializer.NewError(serializer.CodeSignExpired, "签名已过期", nil)
 | 
						ErrExpired    = serializer.NewError(serializer.CodeSignExpired, "签名已过期", nil)
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -280,6 +280,21 @@ func (fs *FileSystem) SetTargetFileByIDs(ids []uint) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetTargetByInterface 根据 model.File 或者 model.Folder 设置目标对象
 | 
				
			||||||
 | 
					// TODO 测试
 | 
				
			||||||
 | 
					func (fs *FileSystem) SetTargetByInterface(target interface{}) error {
 | 
				
			||||||
 | 
						if file, ok := target.(*model.File); ok {
 | 
				
			||||||
 | 
							fs.SetTargetFile(&[]model.File{*file})
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if folder, ok := target.(*model.Folder); ok {
 | 
				
			||||||
 | 
							fs.SetTargetDir(&[]model.Folder{*folder})
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ErrObjectNotExist
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CleanTargets 清空目标
 | 
					// CleanTargets 清空目标
 | 
				
			||||||
func (fs *FileSystem) CleanTargets() {
 | 
					func (fs *FileSystem) CleanTargets() {
 | 
				
			||||||
	fs.FileTarget = fs.FileTarget[:0]
 | 
						fs.FileTarget = fs.FileTarget[:0]
 | 
				
			||||||
 | 
				
			|||||||
@ -46,8 +46,8 @@ const (
 | 
				
			|||||||
	CodeNotFullySuccess = 203
 | 
						CodeNotFullySuccess = 203
 | 
				
			||||||
	// CodeCheckLogin 未登录
 | 
						// CodeCheckLogin 未登录
 | 
				
			||||||
	CodeCheckLogin = 401
 | 
						CodeCheckLogin = 401
 | 
				
			||||||
	// CodeNoRightErr 未授权访问
 | 
						// CodeNoPermissionErr 未授权访问
 | 
				
			||||||
	CodeNoRightErr = 403
 | 
						CodeNoPermissionErr = 403
 | 
				
			||||||
	// CodeNotFound 资源未找到
 | 
						// CodeNotFound 资源未找到
 | 
				
			||||||
	CodeNotFound = 404
 | 
						CodeNotFound = 404
 | 
				
			||||||
	// CodeUploadFailed 上传出错
 | 
						// CodeUploadFailed 上传出错
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Share 分享序列化
 | 
					// Share 分享序列化
 | 
				
			||||||
type Share struct {
 | 
					type Share struct {
 | 
				
			||||||
 | 
						Key        string        `json:"key"`
 | 
				
			||||||
	Locked     bool          `json:"locked"`
 | 
						Locked     bool          `json:"locked"`
 | 
				
			||||||
	IsDir      bool          `json:"is_dir"`
 | 
						IsDir      bool          `json:"is_dir"`
 | 
				
			||||||
	Score      int           `json:"score"`
 | 
						Score      int           `json:"score"`
 | 
				
			||||||
@ -34,6 +35,7 @@ type shareSource struct {
 | 
				
			|||||||
func BuildShareResponse(share *model.Share, unlocked bool) Share {
 | 
					func BuildShareResponse(share *model.Share, unlocked bool) Share {
 | 
				
			||||||
	creator := share.GetCreator()
 | 
						creator := share.GetCreator()
 | 
				
			||||||
	resp := Share{
 | 
						resp := Share{
 | 
				
			||||||
 | 
							Key:    hashid.HashID(share.ID, hashid.ShareID),
 | 
				
			||||||
		Locked: !unlocked,
 | 
							Locked: !unlocked,
 | 
				
			||||||
		Creator: &shareCreator{
 | 
							Creator: &shareCreator{
 | 
				
			||||||
			Key:       hashid.HashID(creator.ID, hashid.UserID),
 | 
								Key:       hashid.HashID(creator.ID, hashid.UserID),
 | 
				
			||||||
 | 
				
			|||||||
@ -22,6 +22,7 @@ type User struct {
 | 
				
			|||||||
	Avatar         string `json:"avatar"`
 | 
						Avatar         string `json:"avatar"`
 | 
				
			||||||
	CreatedAt      int64  `json:"created_at"`
 | 
						CreatedAt      int64  `json:"created_at"`
 | 
				
			||||||
	PreferredTheme string `json:"preferred_theme"`
 | 
						PreferredTheme string `json:"preferred_theme"`
 | 
				
			||||||
 | 
						Score          int    `json:"score"`
 | 
				
			||||||
	Policy         policy `json:"policy"`
 | 
						Policy         policy `json:"policy"`
 | 
				
			||||||
	Group          group  `json:"group"`
 | 
						Group          group  `json:"group"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -41,6 +42,7 @@ type group struct {
 | 
				
			|||||||
	AllowRemoteDownload  bool   `json:"allowRemoteDownload"`
 | 
						AllowRemoteDownload  bool   `json:"allowRemoteDownload"`
 | 
				
			||||||
	AllowTorrentDownload bool   `json:"allowTorrentDownload"`
 | 
						AllowTorrentDownload bool   `json:"allowTorrentDownload"`
 | 
				
			||||||
	AllowArchiveDownload bool   `json:"allowArchiveDownload"`
 | 
						AllowArchiveDownload bool   `json:"allowArchiveDownload"`
 | 
				
			||||||
 | 
						ShareFreeEnabled     bool   `json:"shareFree"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type storage struct {
 | 
					type storage struct {
 | 
				
			||||||
@ -60,6 +62,7 @@ func BuildUser(user model.User) User {
 | 
				
			|||||||
		Avatar:         user.Avatar,
 | 
							Avatar:         user.Avatar,
 | 
				
			||||||
		CreatedAt:      user.CreatedAt.Unix(),
 | 
							CreatedAt:      user.CreatedAt.Unix(),
 | 
				
			||||||
		PreferredTheme: user.OptionsSerialized.PreferredTheme,
 | 
							PreferredTheme: user.OptionsSerialized.PreferredTheme,
 | 
				
			||||||
 | 
							Score:          user.Score,
 | 
				
			||||||
		Policy: policy{
 | 
							Policy: policy{
 | 
				
			||||||
			SaveType:       user.Policy.Type,
 | 
								SaveType:       user.Policy.Type,
 | 
				
			||||||
			MaxSize:        fmt.Sprintf("%.2fmb", float64(user.Policy.MaxSize)/(1024*1024)),
 | 
								MaxSize:        fmt.Sprintf("%.2fmb", float64(user.Policy.MaxSize)/(1024*1024)),
 | 
				
			||||||
@ -74,6 +77,7 @@ func BuildUser(user model.User) User {
 | 
				
			|||||||
			AllowRemoteDownload:  aria2Option[0],
 | 
								AllowRemoteDownload:  aria2Option[0],
 | 
				
			||||||
			AllowTorrentDownload: aria2Option[2],
 | 
								AllowTorrentDownload: aria2Option[2],
 | 
				
			||||||
			AllowArchiveDownload: user.Group.OptionsSerialized.ArchiveDownloadEnabled,
 | 
								AllowArchiveDownload: user.Group.OptionsSerialized.ArchiveDownloadEnabled,
 | 
				
			||||||
 | 
								ShareFreeEnabled:     user.Group.OptionsSerialized.ShareFreeEnabled,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -26,3 +26,14 @@ func GetShare(c *gin.Context) {
 | 
				
			|||||||
		c.JSON(200, ErrorResponse(err))
 | 
							c.JSON(200, ErrorResponse(err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetShareDownload 创建分享下载会话
 | 
				
			||||||
 | 
					func GetShareDownload(c *gin.Context) {
 | 
				
			||||||
 | 
						var service share.SingleFileService
 | 
				
			||||||
 | 
						if err := c.ShouldBindQuery(&service); err == nil {
 | 
				
			||||||
 | 
							res := service.CreateDownloadSession(c)
 | 
				
			||||||
 | 
							c.JSON(200, res)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							c.JSON(200, ErrorResponse(err))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -172,6 +172,8 @@ func InitMasterRouter() *gin.Engine {
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			// 获取分享
 | 
								// 获取分享
 | 
				
			||||||
			share.GET(":id", controllers.GetShare)
 | 
								share.GET(":id", controllers.GetShare)
 | 
				
			||||||
 | 
								// 创建文件下载会话
 | 
				
			||||||
 | 
								share.POST("download/:id", controllers.GetShareDownload)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// 需要登录保护的
 | 
							// 需要登录保护的
 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,7 @@ func (service *ShareCreateService) Create(c *gin.Context) serializer.Response {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// 是否拥有权限
 | 
						// 是否拥有权限
 | 
				
			||||||
	if !user.Group.ShareEnabled {
 | 
						if !user.Group.ShareEnabled {
 | 
				
			||||||
		return serializer.Err(serializer.CodeNoRightErr, "您无权创建分享链接", nil)
 | 
							return serializer.Err(serializer.CodeNoPermissionErr, "您无权创建分享链接", nil)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 对象是否存在
 | 
						// 对象是否存在
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,10 @@
 | 
				
			|||||||
package share
 | 
					package share
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	model "github.com/HFO4/cloudreve/models"
 | 
						model "github.com/HFO4/cloudreve/models"
 | 
				
			||||||
 | 
						"github.com/HFO4/cloudreve/pkg/filesystem"
 | 
				
			||||||
	"github.com/HFO4/cloudreve/pkg/serializer"
 | 
						"github.com/HFO4/cloudreve/pkg/serializer"
 | 
				
			||||||
	"github.com/HFO4/cloudreve/pkg/util"
 | 
						"github.com/HFO4/cloudreve/pkg/util"
 | 
				
			||||||
	"github.com/gin-gonic/gin"
 | 
						"github.com/gin-gonic/gin"
 | 
				
			||||||
@ -13,8 +15,14 @@ type ShareGetService struct {
 | 
				
			|||||||
	Password string `form:"password" binding:"max=255"`
 | 
						Password string `form:"password" binding:"max=255"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SingleFileService 对单文件进行操作的服务,path为可选文件完整路径
 | 
				
			||||||
 | 
					type SingleFileService struct {
 | 
				
			||||||
 | 
						Path string `form:"path" binding:"max=65535"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Get 获取分享内容
 | 
					// Get 获取分享内容
 | 
				
			||||||
func (service *ShareGetService) Get(c *gin.Context) serializer.Response {
 | 
					func (service *ShareGetService) Get(c *gin.Context) serializer.Response {
 | 
				
			||||||
 | 
						user := currentUser(c)
 | 
				
			||||||
	share := model.GetShareByHashID(c.Param("id"))
 | 
						share := model.GetShareByHashID(c.Param("id"))
 | 
				
			||||||
	if share == nil || !share.IsAvailable() {
 | 
						if share == nil || !share.IsAvailable() {
 | 
				
			||||||
		return serializer.Err(serializer.CodeNotFound, "分享不存在或已被取消", nil)
 | 
							return serializer.Err(serializer.CodeNotFound, "分享不存在或已被取消", nil)
 | 
				
			||||||
@ -34,8 +42,62 @@ func (service *ShareGetService) Get(c *gin.Context) serializer.Response {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if unlocked {
 | 
				
			||||||
 | 
							share.Viewed()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 如果已经下载过,不需要付积分
 | 
				
			||||||
 | 
						if share.WasDownloadedBy(user) {
 | 
				
			||||||
 | 
							share.Score = 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return serializer.Response{
 | 
						return serializer.Response{
 | 
				
			||||||
		Code: 0,
 | 
							Code: 0,
 | 
				
			||||||
		Data: serializer.BuildShareResponse(share, unlocked),
 | 
							Data: serializer.BuildShareResponse(share, unlocked),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CreateDownloadSession 创建下载会话
 | 
				
			||||||
 | 
					func (service *SingleFileService) CreateDownloadSession(c *gin.Context) serializer.Response {
 | 
				
			||||||
 | 
						user := currentUser(c)
 | 
				
			||||||
 | 
						share := model.GetShareByHashID(c.Param("id"))
 | 
				
			||||||
 | 
						if share == nil || !share.IsAvailable() {
 | 
				
			||||||
 | 
							return serializer.Err(serializer.CodeNotFound, "分享不存在或已被取消", nil)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 检查用户是否可以下载此分享的文件
 | 
				
			||||||
 | 
						err := share.CanBeDownloadBy(user)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return serializer.Err(serializer.CodeNoPermissionErr, err.Error(), nil)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 对积分、下载次数进行更新
 | 
				
			||||||
 | 
						err = share.DownloadBy(user)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return serializer.Err(serializer.CodeNoPermissionErr, err.Error(), nil)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 创建文件系统
 | 
				
			||||||
 | 
						fs, err := filesystem.NewFileSystem(user)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer fs.Recycle()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 重设文件系统处理目标为源文件
 | 
				
			||||||
 | 
						err = fs.SetTargetByInterface(share.GetSource())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return serializer.Err(serializer.CodePolicyNotAllowed, "源文件不存在", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 取得下载地址
 | 
				
			||||||
 | 
						downloadURL, err := fs.GetDownloadURL(context.Background(), "", "download_timeout")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return serializer.Err(serializer.CodeNotSet, err.Error(), err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return serializer.Response{
 | 
				
			||||||
 | 
							Code: 0,
 | 
				
			||||||
 | 
							Data: downloadURL,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user