Feat: list/search/update/delete shares

pull/247/head
HFO4 4 years ago
parent bb63ea7142
commit cfbfbc3c55

@ -8,6 +8,30 @@ import (
"github.com/gin-gonic/gin"
)
// ShareOwner 检查当前登录用户是否为分享所有者
func ShareOwner() gin.HandlerFunc {
return func(c *gin.Context) {
var user *model.User
if userCtx, ok := c.Get("user"); ok {
user = userCtx.(*model.User)
} else {
c.JSON(200, serializer.Err(serializer.CodeCheckLogin, "请先登录", nil))
c.Abort()
return
}
if share, ok := c.Get("share"); ok {
if share.(*model.Share).Creator().ID != user.ID {
c.JSON(200, serializer.Err(serializer.CodeNotFound, "分享不存在", nil))
c.Abort()
return
}
}
c.Next()
}
}
// ShareAvailable 检查分享是否可用
func ShareAvailable() gin.HandlerFunc {
return func(c *gin.Context) {
@ -21,7 +45,7 @@ func ShareAvailable() gin.HandlerFunc {
share := model.GetShareByHashID(c.Param("id"))
if share == nil || !share.IsAvailable() {
c.JSON(200, serializer.Err(serializer.CodeNotFound, "分享不存在或已被取消", nil))
c.JSON(200, serializer.Err(serializer.CodeNotFound, "分享不存在或已失效", nil))
c.Abort()
return
}

@ -25,6 +25,7 @@ type Share struct {
Expires *time.Time // 过期时间,空值表示无过期时间
Score int // 每人次下载扣除积分
PreviewEnabled bool // 是否允许直接预览
SourceName string `gorm:"index:source"` // 用于搜索的字段
// 数据库忽略字段
User User `gorm:"PRELOAD:false,association_autoupdate:false"`
@ -79,6 +80,11 @@ func (share *Share) IsAvailable() bool {
return false
}
// 检查创建者状态
if share.Creator().Status != Active {
return false
}
return true
}
@ -202,3 +208,50 @@ func (share *Share) Downloaded() {
"remain_downloads": share.RemainDownloads,
})
}
// Update 更新分享属性
func (share *Share) Update(props map[string]interface{}) error {
return DB.Model(share).Updates(props).Error
}
// Delete 删除分享
func (share *Share) Delete() error {
return DB.Model(share).Delete(share).Error
}
// ListShares 列出UID下的分享
func ListShares(uid uint, page, pageSize int, order string, publicOnly bool) ([]Share, int) {
var (
shares []Share
total int
)
dbChain := DB
dbChain = dbChain.Where("user_id = ?", uid)
if publicOnly {
dbChain.Where("password = ?", "")
}
// 计算总数用于分页
dbChain.Model(&Share{}).Count(&total)
// 查询记录
dbChain.Limit(pageSize).Offset((page - 1) * pageSize).Order(order).Find(&shares)
return shares, total
}
// SearchShares 根据关键字搜索分享
func SearchShares(page, pageSize int, order, keywords string) ([]Share, int) {
var (
shares []Share
total int
)
dbChain := DB
dbChain = dbChain.Where("password = ? and remain_downloads <> 0 and (expires is NULL or expires > ?) and source_name like ?", "", time.Now(), "%"+keywords+"%")
// 计算总数用于分页
dbChain.Model(&Share{}).Count(&total)
// 查询记录
dbChain.Limit(pageSize).Offset((page - 1) * pageSize).Order(order).Find(&shares)
return shares, total
}

@ -18,6 +18,8 @@ const (
NotActivicated
// Baned 被封禁
Baned
// OveruseBaned 超额使用被封禁
OveruseBaned
)
// User 用户模型

@ -6,7 +6,7 @@ import (
"time"
)
// Share 分享序列化
// Share 分享信息序列化
type Share struct {
Key string `json:"key"`
Locked bool `json:"locked"`
@ -32,6 +32,64 @@ type shareSource struct {
Size uint64 `json:"size"`
}
// myShareItem 我的分享列表条目
type myShareItem struct {
Key string `json:"key"`
IsDir bool `json:"is_dir"`
Score int `json:"score"`
Password string `json:"password"`
CreateDate string `json:"create_date,omitempty"`
Downloads int `json:"downloads"`
RemainDownloads int `json:"remain_downloads"`
Views int `json:"views"`
Expire int64 `json:"expire"`
Preview bool `json:"preview"`
Source *shareSource `json:"source,omitempty"`
}
// BuildShareList 构建我的分享列表响应
func BuildShareList(shares []model.Share, total int) Response {
res := make([]myShareItem, 0, total)
now := time.Now().Unix()
for i := 0; i < len(shares); i++ {
item := myShareItem{
Key: hashid.HashID(shares[i].ID, hashid.ShareID),
IsDir: shares[i].IsDir,
Score: shares[i].Score,
Password: shares[i].Password,
CreateDate: shares[i].CreatedAt.Format("2006-01-02 15:04:05"),
Downloads: shares[i].Downloads,
Views: shares[i].Views,
Preview: shares[i].PreviewEnabled,
Expire: -1,
RemainDownloads: shares[i].RemainDownloads,
}
if shares[i].Expires != nil {
item.Expire = shares[i].Expires.Unix() - now
if item.Expire == 0 {
item.Expire = 0
}
}
if shares[i].File.ID != 0 {
item.Source = &shareSource{
Name: shares[i].File.Name,
Size: shares[i].File.Size,
}
} else if shares[i].Folder.ID != 0 {
item.Source = &shareSource{
Name: shares[i].Folder.Name,
}
}
res = append(res, item)
}
return Response{Data: map[string]interface{}{
"total": total,
"items": res,
}}
}
// BuildShareResponse 构建获取分享信息响应
func BuildShareResponse(share *model.Share, unlocked bool) Share {
creator := share.Creator()

@ -33,6 +33,50 @@ func GetShare(c *gin.Context) {
}
}
// ListShare 列出分享
func ListShare(c *gin.Context) {
var service share.ShareListService
if err := c.ShouldBindQuery(&service); err == nil {
res := service.List(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// SearchShare 搜索分享
func SearchShare(c *gin.Context) {
var service share.ShareListService
if err := c.ShouldBindQuery(&service); err == nil {
res := service.Search(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// UpdateShare 更新分享属性
func UpdateShare(c *gin.Context) {
var service share.ShareUpdateService
if err := c.ShouldBindJSON(&service); err == nil {
res := service.Update(c)
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// DeleteShare 删除分享
func DeleteShare(c *gin.Context) {
var service share.Service
if err := c.ShouldBindUri(&service); err == nil {
res := service.Delete(c, CurrentUser(c))
c.JSON(200, res)
} else {
c.JSON(200, ErrorResponse(err))
}
}
// GetShareDownload 创建分享下载会话
func GetShareDownload(c *gin.Context) {
var service share.Service

@ -320,6 +320,10 @@ func InitMasterRouter() *gin.Engine {
{
// 创建新分享
share.POST("", controllers.CreateShare)
// 列出我的分享
share.GET("", controllers.ListShare)
// 搜索公共分享
share.GET("search", controllers.SearchShare)
// 转存他人分享
share.POST("save/:id",
middleware.ShareAvailable(),
@ -327,6 +331,16 @@ func InitMasterRouter() *gin.Engine {
middleware.BeforeShareDownload(),
controllers.SaveShare,
)
// 更新分享属性
share.PATCH(":id",
middleware.ShareAvailable(),
middleware.ShareOwner(),
controllers.UpdateShare,
)
// 删除分享
share.DELETE(":id",
controllers.DeleteShare,
)
}
// 用户标签

@ -124,7 +124,11 @@ func ProcessCallback(service CallbackProcessService, c *gin.Context) serializer.
// 获取父目录
exist, parentFolder := fs.IsPathExist(callbackSession.VirtualPath)
if !exist {
return serializer.Err(serializer.CodeParamErr, "指定目录不存在", nil)
newFolder, err := fs.CreateDirectory(context.Background(), callbackSession.VirtualPath)
if err != nil {
return serializer.Err(serializer.CodeParamErr, "指定目录不存在", err)
}
parentFolder = newFolder
}
// 创建文件头

@ -20,6 +20,52 @@ type ShareCreateService struct {
Preview bool `json:"preview"`
}
// ShareUpdateService 分享更新服务
type ShareUpdateService struct {
Prop string `json:"prop" binding:"required,eq=password|eq=preview_enabled"`
Value string `json:"value" binding:"max=255"`
}
// Delete 删除分享
func (service *Service) Delete(c *gin.Context, user *model.User) serializer.Response {
share := model.GetShareByHashID(c.Param("id"))
if share == nil || share.Creator().ID != user.ID {
return serializer.Err(serializer.CodeNotFound, "分享不存在", nil)
}
if err := share.Delete(); err != nil {
return serializer.Err(serializer.CodeDBError, "分享删除失败", err)
}
return serializer.Response{}
}
// Update 更新分享属性
func (service *ShareUpdateService) Update(c *gin.Context) serializer.Response {
shareCtx, _ := c.Get("share")
share := shareCtx.(*model.Share)
switch service.Prop {
case "password":
err := share.Update(map[string]interface{}{"password": service.Value})
if err != nil {
return serializer.Err(serializer.CodeDBError, "无法更新分享密码", err)
}
case "preview_enabled":
value := service.Value == "true"
err := share.Update(map[string]interface{}{"preview_enabled": value})
if err != nil {
return serializer.Err(serializer.CodeDBError, "无法更新分享属性", err)
}
return serializer.Response{
Data: value,
}
}
return serializer.Response{
Data: service.Value,
}
}
// Create 创建新分享
func (service *ShareCreateService) Create(c *gin.Context) serializer.Response {
userCtx, _ := c.Get("user")
@ -32,8 +78,9 @@ func (service *ShareCreateService) Create(c *gin.Context) serializer.Response {
// 源对象真实ID
var (
sourceID uint
err error
sourceID uint
sourceName string
err error
)
if service.IsDir {
sourceID, err = hashid.DecodeHashID(service.SourceID, hashid.FolderID)
@ -50,11 +97,15 @@ func (service *ShareCreateService) Create(c *gin.Context) serializer.Response {
folder, err := model.GetFoldersByIDs([]uint{sourceID}, user.ID)
if err != nil || len(folder) == 0 {
exist = false
} else {
sourceName = folder[0].Name
}
} else {
file, err := model.GetFilesByIDs([]uint{sourceID}, user.ID)
if err != nil || len(file) == 0 {
exist = false
} else {
sourceName = file[0].Name
}
}
if !exist {
@ -69,6 +120,7 @@ func (service *ShareCreateService) Create(c *gin.Context) serializer.Response {
Score: service.Score,
RemainDownloads: -1,
PreviewEnabled: service.Preview,
SourceName: sourceName,
}
// 如果开启了自动过期

@ -33,6 +33,40 @@ type ArchiveService struct {
Dirs []string `json:"dirs" binding:"exists"`
}
// ShareListService 列出分享
type ShareListService struct {
Page uint `form:"page" binding:"required,min=1"`
OrderBy string `form:"order_by" binding:"required,eq=created_at|eq=downloads|eq=views"`
Order string `form:"order" binding:"required,eq=DESC|eq=ASC"`
Keywords string `form:"keywords"`
}
// Search 搜索公共分享
func (service *ShareListService) Search(c *gin.Context) serializer.Response {
// 列出分享
shares, total := model.SearchShares(int(service.Page), 18, service.OrderBy+" "+
service.Order, service.Keywords)
// 列出分享对应的文件
for i := 0; i < len(shares); i++ {
shares[i].Source()
}
return serializer.BuildShareList(shares, total)
}
// List 列出用户分享
func (service *ShareListService) List(c *gin.Context, user *model.User) serializer.Response {
// 列出分享
shares, total := model.ListShares(user.ID, int(service.Page), 18, service.OrderBy+" "+
service.Order, false)
// 列出分享对应的文件
for i := 0; i < len(shares); i++ {
shares[i].Source()
}
return serializer.BuildShareList(shares, total)
}
// Get 获取分享内容
func (service *ShareGetService) Get(c *gin.Context) serializer.Response {
shareCtx, _ := c.Get("share")

Loading…
Cancel
Save