mirror of
https://github.com/OpenListTeam/OpenList.git
synced 2026-10-10 21:13:10 +08:00
171 lines
5.1 KiB
Go
171 lines
5.1 KiB
Go
package mcp
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
stdpath "path"
|
|
"strings"
|
|
|
|
"github.com/OpenListTeam/OpenList/v4/internal/conf"
|
|
"github.com/OpenListTeam/OpenList/v4/internal/errs"
|
|
"github.com/OpenListTeam/OpenList/v4/internal/fs"
|
|
"github.com/OpenListTeam/OpenList/v4/internal/model"
|
|
"github.com/OpenListTeam/OpenList/v4/internal/op"
|
|
"github.com/OpenListTeam/OpenList/v4/internal/setting"
|
|
"github.com/OpenListTeam/OpenList/v4/internal/sign"
|
|
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
|
|
"github.com/OpenListTeam/OpenList/v4/server/common"
|
|
"github.com/OpenListTeam/OpenList/v4/server/handles"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type fsGetArgs struct {
|
|
Path string `json:"path"`
|
|
Password string `json:"password"`
|
|
}
|
|
|
|
func (s *Server) callFSGet(c *gin.Context, raw json.RawMessage) (any, *rpcError) {
|
|
args, mcpErr := parseFSGetArgs(raw)
|
|
if mcpErr != nil {
|
|
return nil, mcpErr
|
|
}
|
|
|
|
user, ok := c.Request.Context().Value(conf.UserKey).(*model.User)
|
|
if !ok || user == nil {
|
|
return nil, &rpcError{Code: -32603, Message: "missing user context"}
|
|
}
|
|
if user.IsGuest() && user.Disabled {
|
|
return nil, &rpcError{Code: -32001, Message: "guest user is disabled"}
|
|
}
|
|
|
|
reqPath, err := user.JoinPath(args.Path)
|
|
if err != nil {
|
|
return nil, &rpcError{Code: -32003, Message: err.Error()}
|
|
}
|
|
|
|
meta, err := op.GetNearestMeta(reqPath)
|
|
if err != nil && !errors.Is(errors.Cause(err), errs.MetaNotFound) {
|
|
return nil, &rpcError{Code: -32603, Message: err.Error()}
|
|
}
|
|
if !common.CanAccess(user, meta, reqPath, args.Password) {
|
|
return nil, &rpcError{Code: -32003, Message: "password is incorrect or you have no permission"}
|
|
}
|
|
|
|
ctx := context.WithValue(c.Request.Context(), conf.MetaKey, meta)
|
|
obj, err := fs.Get(ctx, reqPath, &fs.GetArgs{
|
|
WithStorageDetails: !user.IsGuest() && !setting.GetBool(conf.HideStorageDetails),
|
|
})
|
|
if err != nil {
|
|
return nil, &rpcError{Code: -32603, Message: err.Error()}
|
|
}
|
|
|
|
rawURL, provider, err := buildFSGetRawURL(ctx, c, reqPath, obj, meta)
|
|
if err != nil {
|
|
return nil, &rpcError{Code: -32603, Message: err.Error()}
|
|
}
|
|
|
|
parentPath := stdpath.Dir(reqPath)
|
|
var related []model.Obj
|
|
if !obj.IsDir() && utils.GetFileType(obj.GetName()) == conf.VIDEO {
|
|
sameLevelFiles, err := fs.List(ctx, parentPath, &fs.ListArgs{})
|
|
if err == nil {
|
|
related = filterRelatedObjs(sameLevelFiles, obj)
|
|
}
|
|
}
|
|
|
|
parentMeta, _ := op.GetNearestMeta(parentPath)
|
|
thumb, _ := model.GetThumb(obj)
|
|
mountDetails, _ := model.GetStorageDetails(obj)
|
|
return handles.FsGetResp{
|
|
ObjResp: handles.ObjResp{
|
|
Name: obj.GetName(),
|
|
Size: obj.GetSize(),
|
|
IsDir: obj.IsDir(),
|
|
Modified: obj.ModTime(),
|
|
Created: obj.CreateTime(),
|
|
Sign: common.Sign(obj, parentPath, isEncrypt(meta, reqPath)),
|
|
Thumb: thumb,
|
|
Type: utils.GetObjType(obj.GetName(), obj.IsDir()),
|
|
HashInfoStr: obj.GetHash().String(),
|
|
HashInfo: obj.GetHash().Export(),
|
|
MountDetails: mountDetails,
|
|
},
|
|
RawURL: rawURL,
|
|
Readme: getReadme(meta, reqPath),
|
|
Header: getHeader(meta, reqPath),
|
|
Provider: provider,
|
|
Related: toObjResp(related, parentPath, isEncrypt(parentMeta, parentPath)),
|
|
}, nil
|
|
}
|
|
|
|
func parseFSGetArgs(raw json.RawMessage) (*fsGetArgs, *rpcError) {
|
|
args := &fsGetArgs{}
|
|
if len(raw) == 0 || string(raw) == "null" {
|
|
return nil, &rpcError{Code: -32602, Message: "invalid openlist.fs.get arguments"}
|
|
}
|
|
|
|
if err := json.Unmarshal(raw, args); err != nil {
|
|
return nil, &rpcError{Code: -32602, Message: "invalid openlist.fs.get arguments"}
|
|
}
|
|
if args.Path == "" {
|
|
return nil, &rpcError{Code: -32602, Message: "path is required"}
|
|
}
|
|
return args, nil
|
|
}
|
|
|
|
func buildFSGetRawURL(ctx context.Context, c *gin.Context, reqPath string, obj model.Obj, meta *model.Meta) (string, string, error) {
|
|
storage, storageErr := fs.GetStorage(reqPath, &fs.GetStoragesArgs{})
|
|
provider, ok := model.GetProvider(obj)
|
|
if !ok && storageErr == nil {
|
|
provider = storage.Config().Name
|
|
}
|
|
if obj.IsDir() {
|
|
return "", provider, nil
|
|
}
|
|
if storageErr != nil {
|
|
return "", provider, storageErr
|
|
}
|
|
|
|
if storage.Config().MustProxy() || storage.GetStorage().WebProxy {
|
|
rawURL := common.GenerateDownProxyURL(storage.GetStorage(), reqPath)
|
|
if rawURL != "" {
|
|
return rawURL, provider, nil
|
|
}
|
|
query := ""
|
|
if isEncrypt(meta, reqPath) || setting.GetBool(conf.SignAll) {
|
|
query = "?sign=" + sign.Sign(reqPath)
|
|
}
|
|
return fmt.Sprintf("%s/p%s%s", common.GetApiUrl(ctx), utils.EncodePath(reqPath, true), query), provider, nil
|
|
}
|
|
|
|
if url, ok := model.GetUrl(obj); ok {
|
|
return url, provider, nil
|
|
}
|
|
link, _, err := fs.Link(ctx, reqPath, model.LinkArgs{
|
|
IP: c.ClientIP(),
|
|
Header: c.Request.Header,
|
|
Redirect: true,
|
|
})
|
|
if err != nil {
|
|
return "", provider, err
|
|
}
|
|
defer link.Close()
|
|
return link.URL, provider, nil
|
|
}
|
|
|
|
func filterRelatedObjs(objs []model.Obj, obj model.Obj) []model.Obj {
|
|
related := make([]model.Obj, 0)
|
|
nameWithoutExt := strings.TrimSuffix(obj.GetName(), stdpath.Ext(obj.GetName()))
|
|
for _, current := range objs {
|
|
if current.GetName() == obj.GetName() {
|
|
continue
|
|
}
|
|
if strings.HasPrefix(current.GetName(), nameWithoutExt) {
|
|
related = append(related, current)
|
|
}
|
|
}
|
|
return related
|
|
}
|