alist/server/handles/fsread.go

312 lines
7.5 KiB
Go
Raw Normal View History

2022-07-11 17:12:50 +08:00
package handles
2022-06-27 19:51:23 +08:00
import (
2022-06-29 16:23:31 +08:00
"fmt"
2022-06-28 18:12:53 +08:00
stdpath "path"
2022-06-29 16:23:31 +08:00
"strings"
2022-06-28 18:12:53 +08:00
"time"
2022-06-27 19:51:23 +08:00
"github.com/alist-org/alist/v3/internal/db"
2022-06-28 18:00:11 +08:00
"github.com/alist-org/alist/v3/internal/errs"
2022-06-27 19:51:23 +08:00
"github.com/alist-org/alist/v3/internal/fs"
"github.com/alist-org/alist/v3/internal/model"
2022-08-03 13:03:45 +08:00
"github.com/alist-org/alist/v3/internal/sign"
2022-06-27 19:51:23 +08:00
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/alist-org/alist/v3/server/common"
"github.com/gin-gonic/gin"
2022-06-28 18:00:11 +08:00
"github.com/pkg/errors"
2022-06-27 19:51:23 +08:00
)
type ListReq struct {
common.PageReq
Path string `json:"path" form:"path"`
Password string `json:"password" form:"password"`
}
2022-07-10 14:09:31 +08:00
type DirReq struct {
Path string `json:"path" form:"path"`
Password string `json:"password" form:"password"`
}
2022-06-27 19:51:23 +08:00
type ObjResp struct {
2022-08-03 13:03:45 +08:00
Name string `json:"name"`
Size int64 `json:"size"`
IsDir bool `json:"is_dir"`
Modified time.Time `json:"modified"`
Sign string `json:"sign"`
Thumbnail string `json:"thumbnail"`
2022-06-27 19:51:23 +08:00
}
2022-06-27 21:15:39 +08:00
type FsListResp struct {
2022-06-27 19:51:23 +08:00
Content []ObjResp `json:"content"`
Total int64 `json:"total"`
2022-06-30 15:41:58 +08:00
Readme string `json:"readme"`
2022-06-30 15:53:57 +08:00
Write bool `json:"write"`
2022-06-27 19:51:23 +08:00
}
2022-06-27 21:15:39 +08:00
func FsList(c *gin.Context) {
2022-06-27 19:51:23 +08:00
var req ListReq
if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400)
return
}
req.Validate()
user := c.MustGet("user").(*model.User)
2022-06-27 20:37:05 +08:00
req.Path = stdpath.Join(user.BasePath, req.Path)
2022-06-28 18:00:11 +08:00
meta, err := db.GetNearestMeta(req.Path)
if err != nil {
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
common.ErrorResp(c, err, 500, true)
return
}
}
2022-06-27 19:51:23 +08:00
c.Set("meta", meta)
if !canAccess(user, meta, req.Path, req.Password) {
2022-07-10 14:09:31 +08:00
common.ErrorStrResp(c, "password is incorrect", 403)
2022-06-27 19:51:23 +08:00
return
}
objs, err := fs.List(c, req.Path)
if err != nil {
2022-06-28 18:12:53 +08:00
common.ErrorResp(c, err, 500)
2022-06-27 19:51:23 +08:00
return
}
total, objs := pagination(objs, &req.PageReq)
2022-06-27 21:15:39 +08:00
common.SuccessResp(c, FsListResp{
2022-06-29 16:08:55 +08:00
Content: toObjResp(objs),
2022-06-27 19:51:23 +08:00
Total: int64(total),
2022-06-30 15:41:58 +08:00
Readme: getReadme(meta, req.Path),
2022-06-30 15:53:57 +08:00
Write: user.CanWrite() || canWrite(meta, req.Path),
2022-06-27 19:51:23 +08:00
})
}
2022-07-10 14:09:31 +08:00
func FsDirs(c *gin.Context) {
var req DirReq
if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400)
return
}
user := c.MustGet("user").(*model.User)
req.Path = stdpath.Join(user.BasePath, req.Path)
meta, err := db.GetNearestMeta(req.Path)
if err != nil {
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
common.ErrorResp(c, err, 500, true)
return
}
}
c.Set("meta", meta)
if !canAccess(user, meta, req.Path, req.Password) {
common.ErrorStrResp(c, "password is incorrect", 403)
return
}
objs, err := fs.List(c, req.Path)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
dirs := filterDirs(objs)
common.SuccessResp(c, dirs)
}
type DirResp struct {
Name string `json:"name"`
Modified time.Time `json:"modified"`
}
func filterDirs(objs []model.Obj) []DirResp {
var dirs []DirResp
for _, obj := range objs {
if obj.IsDir() {
dirs = append(dirs, DirResp{
Name: obj.GetName(),
Modified: obj.ModTime(),
})
}
}
return dirs
}
2022-06-30 15:41:58 +08:00
func getReadme(meta *model.Meta, path string) string {
if meta != nil && (utils.PathEqual(meta.Path, path) || meta.RSub) {
return meta.Readme
}
return ""
}
2022-06-27 19:51:23 +08:00
func canAccess(user *model.User, meta *model.Meta, path string, password string) bool {
// if is not guest, can access
2022-06-29 18:03:12 +08:00
if user.CanAccessWithoutPassword() {
2022-06-27 19:51:23 +08:00
return true
}
// if meta is nil or password is empty, can access
if meta == nil || meta.Password == "" {
return true
}
// if meta doesn't apply to sub_folder, can access
2022-06-30 15:41:58 +08:00
if !utils.PathEqual(meta.Path, path) && !meta.PSub {
2022-06-27 19:51:23 +08:00
return true
}
// validate password
return meta.Password == password
}
func pagination(objs []model.Obj, req *common.PageReq) (int, []model.Obj) {
pageIndex, pageSize := req.PageIndex, req.PageSize
total := len(objs)
start := (pageIndex - 1) * pageSize
if start > total {
return total, []model.Obj{}
}
end := start + pageSize
if end > total {
end = total
}
return total, objs[start:end]
}
2022-06-29 16:08:55 +08:00
func toObjResp(objs []model.Obj) []ObjResp {
2022-06-27 19:51:23 +08:00
var resp []ObjResp
for _, obj := range objs {
2022-08-03 13:03:45 +08:00
thumbnail := ""
if t, ok := obj.(model.Thumbnail); ok {
thumbnail = t.Thumbnail()
}
2022-06-27 19:51:23 +08:00
resp = append(resp, ObjResp{
2022-08-03 13:03:45 +08:00
Name: obj.GetName(),
Size: obj.GetSize(),
IsDir: obj.IsDir(),
Modified: obj.ModTime(),
Sign: common.Sign(obj),
Thumbnail: thumbnail,
2022-06-27 19:51:23 +08:00
})
}
return resp
}
2022-06-29 16:08:55 +08:00
2022-06-29 17:08:31 +08:00
type FsGetOrLinkReq struct {
2022-06-29 16:08:55 +08:00
Path string `json:"path" form:"path"`
Password string `json:"password" form:"password"`
}
type FsGetResp struct {
ObjResp
2022-08-07 21:01:29 +08:00
RawURL string `json:"raw_url"`
Readme string `json:"readme"`
Related []string `json:"related"`
2022-06-29 16:08:55 +08:00
}
func FsGet(c *gin.Context) {
2022-06-29 17:08:31 +08:00
var req FsGetOrLinkReq
2022-06-29 16:08:55 +08:00
if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400)
return
}
user := c.MustGet("user").(*model.User)
req.Path = stdpath.Join(user.BasePath, req.Path)
meta, err := db.GetNearestMeta(req.Path)
if err != nil {
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
common.ErrorResp(c, err, 500)
return
}
}
c.Set("meta", meta)
if !canAccess(user, meta, req.Path, req.Password) {
2022-07-10 14:09:31 +08:00
common.ErrorStrResp(c, "password is incorrect", 403)
2022-06-29 16:08:55 +08:00
return
}
obj, err := fs.Get(c, req.Path)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
2022-06-29 16:23:31 +08:00
var rawURL string
2022-07-08 15:56:29 +08:00
// file have raw url
if !obj.IsDir() {
if u, ok := obj.(model.URL); ok {
rawURL = u.URL()
2022-06-29 16:23:31 +08:00
} else {
2022-07-10 14:45:39 +08:00
storage, _ := fs.GetStorage(req.Path)
if storage.Config().MustProxy() || storage.GetStorage().WebProxy {
if storage.GetStorage().DownProxyUrl != "" {
rawURL = fmt.Sprintf("%s%s?sign=%s", strings.Split(storage.GetStorage().DownProxyUrl, "\n")[0], req.Path, sign.Sign(obj.GetName()))
2022-07-08 15:56:29 +08:00
} else {
rawURL = fmt.Sprintf("%s/p%s?sign=%s", common.GetBaseUrl(c.Request), req.Path, sign.Sign(obj.GetName()))
}
} else {
2022-07-10 14:45:39 +08:00
// if storage is not proxy, use raw url by fs.Link
link, _, err := fs.Link(c, req.Path, model.LinkArgs{IP: c.ClientIP()})
2022-07-08 15:56:29 +08:00
if err != nil {
common.ErrorResp(c, err, 500)
return
}
rawURL = link.URL
2022-06-29 16:23:31 +08:00
}
}
}
2022-08-07 21:01:29 +08:00
var related []string
sameLevelFiles, err := fs.List(c, stdpath.Dir(req.Path))
if err == nil {
related = filterRelated(sameLevelFiles, obj)
}
2022-06-29 16:08:55 +08:00
common.SuccessResp(c, FsGetResp{
ObjResp: ObjResp{
Name: obj.GetName(),
Size: obj.GetSize(),
IsDir: obj.IsDir(),
Modified: obj.ModTime(),
Sign: common.Sign(obj),
},
2022-08-07 21:01:29 +08:00
RawURL: rawURL,
Readme: getReadme(meta, req.Path),
Related: related,
2022-06-29 16:08:55 +08:00
})
}
2022-08-03 14:14:37 +08:00
2022-08-07 21:01:29 +08:00
func filterRelated(objs []model.Obj, obj model.Obj) []string {
var related []string
nameWithoutExt := strings.TrimSuffix(obj.GetName(), stdpath.Ext(obj.GetName()))
for _, o := range objs {
if o.GetName() == obj.GetName() {
continue
}
if strings.HasPrefix(o.GetName(), nameWithoutExt) {
related = append(related, o.GetName())
}
}
return related
}
2022-08-03 14:14:37 +08:00
type FsOtherReq struct {
model.FsOtherArgs
Password string `json:"password" form:"password"`
}
func FsOther(c *gin.Context) {
var req FsOtherReq
if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400)
return
}
user := c.MustGet("user").(*model.User)
req.Path = stdpath.Join(user.BasePath, req.Path)
meta, err := db.GetNearestMeta(req.Path)
if err != nil {
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
common.ErrorResp(c, err, 500)
return
}
}
c.Set("meta", meta)
if !canAccess(user, meta, req.Path, req.Password) {
common.ErrorStrResp(c, "password is incorrect", 403)
return
}
res, err := fs.Other(c, req.FsOtherArgs)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
common.SuccessResp(c, res)
}