Feat: list/search/update/delete shares
This commit is contained in:
parent
bb63ea7142
commit
cfbfbc3c55
9 changed files with 290 additions and 5 deletions
|
@ -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…
Add table
Reference in a new issue