mirror of
https://github.com/OpenListTeam/OpenList.git
synced 2025-09-19 20:26:26 +08:00

提供一种类似大多数网盘的文件分享操作,这种分享方式可以通过强制 Web 代理隐藏文件源路径,可以设置分享码、最大访问数和过期时间,并且不需要启用 guest 用户。 在全局设置中可以调整: - 是否强制 Web 代理 - 是否允许预览 - 是否允许预览压缩文件 - 分享文件后,点击“复制链接”按钮复制的内容 前端部分:OpenListTeam/OpenList-Frontend#156 文档部分:OpenListTeam/OpenList-Docs#130 Close #183 Close #526 Close #860 Close #892 Close #1079 * feat(share): support more secure file sharing * feat(share): add archive preview * fix(share): fix some bugs * feat(openlist_share): add openlist share driver * fix(share): lack unwrap when get virtual path * fix: use unwrapPath instead of path for virtual file name comparison * fix(share): change request method of /api/share/list from GET to Any * fix(share): path traversal vulnerability in sharing path check * 修复分享alias驱动的文件 没开代理时无法获取URL * fix(sharing): update error message for sharing root link extraction --------- Co-authored-by: Suyunmeng <69945917+Suyunmeng@users.noreply.github.com> Co-authored-by: j2rong4cn <j2rong@qq.com>
140 lines
3.9 KiB
Go
140 lines
3.9 KiB
Go
package op
|
|
|
|
import (
|
|
"fmt"
|
|
stdpath "path"
|
|
"strings"
|
|
|
|
"github.com/OpenListTeam/OpenList/v4/internal/db"
|
|
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
|
"github.com/OpenListTeam/OpenList/v4/pkg/singleflight"
|
|
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
|
"github.com/OpenListTeam/go-cache"
|
|
"github.com/pkg/errors"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
func makeJoined(sdb []model.SharingDB) []model.Sharing {
|
|
creator := make(map[uint]*model.User)
|
|
return utils.MustSliceConvert(sdb, func(s model.SharingDB) model.Sharing {
|
|
var c *model.User
|
|
var ok bool
|
|
if c, ok = creator[s.CreatorId]; !ok {
|
|
var err error
|
|
if c, err = GetUserById(s.CreatorId); err != nil {
|
|
c = nil
|
|
} else {
|
|
creator[s.CreatorId] = c
|
|
}
|
|
}
|
|
var files []string
|
|
if err := utils.Json.UnmarshalFromString(s.FilesRaw, &files); err != nil {
|
|
files = make([]string, 0)
|
|
}
|
|
return model.Sharing{
|
|
SharingDB: &s,
|
|
Files: files,
|
|
Creator: c,
|
|
}
|
|
})
|
|
}
|
|
|
|
var sharingCache = cache.NewMemCache(cache.WithShards[*model.Sharing](8))
|
|
var sharingG singleflight.Group[*model.Sharing]
|
|
|
|
func GetSharingById(id string, refresh ...bool) (*model.Sharing, error) {
|
|
if !utils.IsBool(refresh...) {
|
|
if sharing, ok := sharingCache.Get(id); ok {
|
|
log.Debugf("use cache when get sharing %s", id)
|
|
return sharing, nil
|
|
}
|
|
}
|
|
sharing, err, _ := sharingG.Do(id, func() (*model.Sharing, error) {
|
|
s, err := db.GetSharingById(id)
|
|
if err != nil {
|
|
return nil, errors.WithMessagef(err, "failed get sharing [%s]", id)
|
|
}
|
|
creator, err := GetUserById(s.CreatorId)
|
|
if err != nil {
|
|
return nil, errors.WithMessagef(err, "failed get sharing creator [%s]", id)
|
|
}
|
|
var files []string
|
|
if err = utils.Json.UnmarshalFromString(s.FilesRaw, &files); err != nil {
|
|
files = make([]string, 0)
|
|
}
|
|
return &model.Sharing{
|
|
SharingDB: s,
|
|
Files: files,
|
|
Creator: creator,
|
|
}, nil
|
|
})
|
|
return sharing, err
|
|
}
|
|
|
|
func GetSharings(pageIndex, pageSize int) ([]model.Sharing, int64, error) {
|
|
s, cnt, err := db.GetSharings(pageIndex, pageSize)
|
|
if err != nil {
|
|
return nil, 0, errors.WithStack(err)
|
|
}
|
|
return makeJoined(s), cnt, nil
|
|
}
|
|
|
|
func GetSharingsByCreatorId(userId uint, pageIndex, pageSize int) ([]model.Sharing, int64, error) {
|
|
s, cnt, err := db.GetSharingsByCreatorId(userId, pageIndex, pageSize)
|
|
if err != nil {
|
|
return nil, 0, errors.WithStack(err)
|
|
}
|
|
return makeJoined(s), cnt, nil
|
|
}
|
|
|
|
func GetSharingUnwrapPath(sharing *model.Sharing, path string) (unwrapPath string, err error) {
|
|
if len(sharing.Files) == 0 {
|
|
return "", errors.New("cannot get actual path of an invalid sharing")
|
|
}
|
|
if len(sharing.Files) == 1 {
|
|
return stdpath.Join(sharing.Files[0], path), nil
|
|
}
|
|
path = utils.FixAndCleanPath(path)[1:]
|
|
if len(path) == 0 {
|
|
return "", errors.New("cannot get actual path of a sharing root path")
|
|
}
|
|
mapPath := ""
|
|
child, rest, _ := strings.Cut(path, "/")
|
|
for _, c := range sharing.Files {
|
|
if child == stdpath.Base(c) {
|
|
mapPath = c
|
|
break
|
|
}
|
|
}
|
|
if mapPath == "" {
|
|
return "", fmt.Errorf("failed find child [%s] of sharing [%s]", child, sharing.ID)
|
|
}
|
|
return stdpath.Join(mapPath, rest), nil
|
|
}
|
|
|
|
func CreateSharing(sharing *model.Sharing) (id string, err error) {
|
|
sharing.CreatorId = sharing.Creator.ID
|
|
sharing.FilesRaw, err = utils.Json.MarshalToString(utils.MustSliceConvert(sharing.Files, utils.FixAndCleanPath))
|
|
if err != nil {
|
|
return "", errors.WithStack(err)
|
|
}
|
|
return db.CreateSharing(sharing.SharingDB)
|
|
}
|
|
|
|
func UpdateSharing(sharing *model.Sharing, skipMarshal ...bool) (err error) {
|
|
if !utils.IsBool(skipMarshal...) {
|
|
sharing.CreatorId = sharing.Creator.ID
|
|
sharing.FilesRaw, err = utils.Json.MarshalToString(utils.MustSliceConvert(sharing.Files, utils.FixAndCleanPath))
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
}
|
|
sharingCache.Del(sharing.ID)
|
|
return db.UpdateSharing(sharing.SharingDB)
|
|
}
|
|
|
|
func DeleteSharing(sid string) error {
|
|
sharingCache.Del(sid)
|
|
return db.DeleteSharingById(sid)
|
|
}
|