Files
OpenList/drivers/ipfs_api/driver.go
jerry a4bfbf8a83 fix(ipfs): fix problems (#8252)
* fix: 🐛 (ipfs): fix the list error caused by not proper join path function

使用更加规范的路径拼接,修复了有中文或符号的路径无法正常访问的问题

* refactor: 命名规范

* 删除多余的条件判断

* fix: 使用withresult方法重构代码,添加get方法,提高性能

* fix: 允许get方法获取目录

去除多余的判断

* fix: 允许copy,rename,move进行覆写

* fix: 修复move方法导致的目录被删除

* refactor: 整理关于返回Path的代码

* fix: 修复由于get方法导致的ipfs路径无法访问

* fix: 修复path处理错误的get方法

修复get方法,删除意外加入的目录

* fix: fix path join

use path join instead of filepath join to avoid os problem

* fix: rm filepath ref

---------

Co-authored-by: Andy Hsu <i@nn.ci>
2025-04-12 17:01:30 +08:00

189 lines
5.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package ipfs
import (
"context"
"fmt"
"net/url"
"path"
shell "github.com/ipfs/go-ipfs-api"
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/model"
)
type IPFS struct {
model.Storage
Addition
sh *shell.Shell
gateURL *url.URL
}
func (d *IPFS) Config() driver.Config {
return config
}
func (d *IPFS) GetAddition() driver.Additional {
return &d.Addition
}
func (d *IPFS) Init(ctx context.Context) error {
d.sh = shell.NewShell(d.Endpoint)
gateURL, err := url.Parse(d.Gateway)
if err != nil {
return err
}
d.gateURL = gateURL
return nil
}
func (d *IPFS) Drop(ctx context.Context) error {
return nil
}
func (d *IPFS) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
var ipfsPath string
cid := dir.GetID()
if cid != "" {
ipfsPath = path.Join("/ipfs", cid)
} else {
// 可能出现ipns dns解析失败的情况需要重复获取cid其他情况应该不会出错
ipfsPath = dir.GetPath()
switch d.Mode {
case "ipfs":
ipfsPath = path.Join("/ipfs", ipfsPath)
case "ipns":
ipfsPath = path.Join("/ipns", ipfsPath)
case "mfs":
fileStat, err := d.sh.FilesStat(ctx, ipfsPath)
if err != nil {
return nil, err
}
ipfsPath = path.Join("/ipfs", fileStat.Hash)
default:
return nil, fmt.Errorf("mode error")
}
}
dirs, err := d.sh.List(ipfsPath)
if err != nil {
return nil, err
}
objlist := []model.Obj{}
for _, file := range dirs {
objlist = append(objlist, &model.Object{ID: file.Hash, Name: file.Name, Size: int64(file.Size), IsFolder: file.Type == 1})
}
return objlist, nil
}
func (d *IPFS) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
gateurl := d.gateURL.JoinPath("/ipfs/", file.GetID())
gateurl.RawQuery = "filename=" + url.QueryEscape(file.GetName())
return &model.Link{URL: gateurl.String()}, nil
}
func (d *IPFS) Get(ctx context.Context, rawPath string) (model.Obj, error) {
rawPath = path.Join(d.GetRootPath(), rawPath)
var ipfsPath string
switch d.Mode {
case "ipfs":
ipfsPath = path.Join("/ipfs", rawPath)
case "ipns":
ipfsPath = path.Join("/ipns", rawPath)
case "mfs":
fileStat, err := d.sh.FilesStat(ctx, rawPath)
if err != nil {
return nil, err
}
ipfsPath = path.Join("/ipfs", fileStat.Hash)
default:
return nil, fmt.Errorf("mode error")
}
file, err := d.sh.FilesStat(ctx, ipfsPath)
if err != nil {
return nil, err
}
return &model.Object{ID: file.Hash, Name: path.Base(rawPath), Path: rawPath, Size: int64(file.Size), IsFolder: file.Type == "directory"}, nil
}
func (d *IPFS) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) (model.Obj, error) {
if d.Mode != "mfs" {
return nil, fmt.Errorf("only write in mfs mode")
}
dirPath := parentDir.GetPath()
err := d.sh.FilesMkdir(ctx, path.Join(dirPath, dirName), shell.FilesMkdir.Parents(true))
if err != nil {
return nil, err
}
file, err := d.sh.FilesStat(ctx, path.Join(dirPath, dirName))
if err != nil {
return nil, err
}
return &model.Object{ID: file.Hash, Name: dirName, Path: path.Join(dirPath, dirName), Size: int64(file.Size), IsFolder: true}, nil
}
func (d *IPFS) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
if d.Mode != "mfs" {
return nil, fmt.Errorf("only write in mfs mode")
}
dstPath := path.Join(dstDir.GetPath(), path.Base(srcObj.GetPath()))
d.sh.FilesRm(ctx, dstPath, true)
return &model.Object{ID: srcObj.GetID(), Name: srcObj.GetName(), Path: dstPath, Size: int64(srcObj.GetSize()), IsFolder: srcObj.IsDir()},
d.sh.FilesMv(ctx, srcObj.GetPath(), dstDir.GetPath())
}
func (d *IPFS) Rename(ctx context.Context, srcObj model.Obj, newName string) (model.Obj, error) {
if d.Mode != "mfs" {
return nil, fmt.Errorf("only write in mfs mode")
}
dstPath := path.Join(path.Dir(srcObj.GetPath()), newName)
d.sh.FilesRm(ctx, dstPath, true)
return &model.Object{ID: srcObj.GetID(), Name: newName, Path: dstPath, Size: int64(srcObj.GetSize()),
IsFolder: srcObj.IsDir()}, d.sh.FilesMv(ctx, srcObj.GetPath(), dstPath)
}
func (d *IPFS) Copy(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
if d.Mode != "mfs" {
return nil, fmt.Errorf("only write in mfs mode")
}
dstPath := path.Join(dstDir.GetPath(), path.Base(srcObj.GetPath()))
d.sh.FilesRm(ctx, dstPath, true)
return &model.Object{ID: srcObj.GetID(), Name: srcObj.GetName(), Path: dstPath, Size: int64(srcObj.GetSize()), IsFolder: srcObj.IsDir()},
d.sh.FilesCp(ctx, path.Join("/ipfs/", srcObj.GetID()), dstPath, shell.FilesCp.Parents(true))
}
func (d *IPFS) Remove(ctx context.Context, obj model.Obj) error {
if d.Mode != "mfs" {
return fmt.Errorf("only write in mfs mode")
}
return d.sh.FilesRm(ctx, obj.GetPath(), true)
}
func (d *IPFS) Put(ctx context.Context, dstDir model.Obj, s model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) {
if d.Mode != "mfs" {
return nil, fmt.Errorf("only write in mfs mode")
}
outHash, err := d.sh.Add(driver.NewLimitedUploadStream(ctx, &driver.ReaderUpdatingProgress{
Reader: s,
UpdateProgress: up,
}))
if err != nil {
return nil, err
}
dstPath := path.Join(dstDir.GetPath(), s.GetName())
if s.GetExist() != nil {
d.sh.FilesRm(ctx, dstPath, true)
}
err = d.sh.FilesCp(ctx, path.Join("/ipfs/", outHash), dstPath, shell.FilesCp.Parents(true))
gateurl := d.gateURL.JoinPath("/ipfs/", outHash)
gateurl.RawQuery = "filename=" + url.QueryEscape(s.GetName())
return &model.Object{ID: outHash, Name: s.GetName(), Path: dstPath, Size: int64(s.GetSize()), IsFolder: s.IsDir()}, err
}
//func (d *Template) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
// return nil, errs.NotSupport
//}
var _ driver.Driver = (*IPFS)(nil)