2023-05-05 17:42:22 +08:00
|
|
|
|
package ipfs
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
|
|
|
|
"net/url"
|
2025-04-12 17:01:30 +08:00
|
|
|
|
"path"
|
2023-05-05 17:42:22 +08:00
|
|
|
|
|
2025-03-27 23:25:23 +08:00
|
|
|
|
shell "github.com/ipfs/go-ipfs-api"
|
|
|
|
|
|
2025-06-12 22:02:46 +08:00
|
|
|
|
"github.com/OpenListTeam/OpenList/internal/driver"
|
|
|
|
|
"github.com/OpenListTeam/OpenList/internal/model"
|
2023-05-05 17:42:22 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
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) {
|
2025-04-12 17:01:30 +08:00
|
|
|
|
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")
|
2025-03-27 23:25:23 +08:00
|
|
|
|
}
|
2023-05-05 17:42:22 +08:00
|
|
|
|
}
|
2025-04-12 17:01:30 +08:00
|
|
|
|
dirs, err := d.sh.List(ipfsPath)
|
2023-05-05 17:42:22 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
objlist := []model.Obj{}
|
|
|
|
|
for _, file := range dirs {
|
2025-04-12 17:01:30 +08:00
|
|
|
|
objlist = append(objlist, &model.Object{ID: file.Hash, Name: file.Name, Size: int64(file.Size), IsFolder: file.Type == 1})
|
2023-05-05 17:42:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return objlist, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d *IPFS) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
2025-04-12 17:01:30 +08:00
|
|
|
|
gateurl := d.gateURL.JoinPath("/ipfs/", file.GetID())
|
|
|
|
|
gateurl.RawQuery = "filename=" + url.QueryEscape(file.GetName())
|
2025-03-27 23:25:23 +08:00
|
|
|
|
return &model.Link{URL: gateurl.String()}, nil
|
2023-05-05 17:42:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-04-12 17:01:30 +08:00
|
|
|
|
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) {
|
2025-03-27 23:25:23 +08:00
|
|
|
|
if d.Mode != "mfs" {
|
2025-04-12 17:01:30 +08:00
|
|
|
|
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
|
2025-03-27 23:25:23 +08:00
|
|
|
|
}
|
2025-04-12 17:01:30 +08:00
|
|
|
|
file, err := d.sh.FilesStat(ctx, path.Join(dirPath, dirName))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
2023-05-05 17:42:22 +08:00
|
|
|
|
}
|
2025-04-12 17:01:30 +08:00
|
|
|
|
return &model.Object{ID: file.Hash, Name: dirName, Path: path.Join(dirPath, dirName), Size: int64(file.Size), IsFolder: true}, nil
|
2023-05-05 17:42:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-04-12 17:01:30 +08:00
|
|
|
|
func (d *IPFS) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
|
2025-03-27 23:25:23 +08:00
|
|
|
|
if d.Mode != "mfs" {
|
2025-04-12 17:01:30 +08:00
|
|
|
|
return nil, fmt.Errorf("only write in mfs mode")
|
2025-03-27 23:25:23 +08:00
|
|
|
|
}
|
2025-04-12 17:01:30 +08:00
|
|
|
|
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())
|
2023-05-05 17:42:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-04-12 17:01:30 +08:00
|
|
|
|
func (d *IPFS) Rename(ctx context.Context, srcObj model.Obj, newName string) (model.Obj, error) {
|
2025-03-27 23:25:23 +08:00
|
|
|
|
if d.Mode != "mfs" {
|
2025-04-12 17:01:30 +08:00
|
|
|
|
return nil, fmt.Errorf("only write in mfs mode")
|
2025-03-27 23:25:23 +08:00
|
|
|
|
}
|
2025-04-12 17:01:30 +08:00
|
|
|
|
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)
|
2023-05-05 17:42:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-04-12 17:01:30 +08:00
|
|
|
|
func (d *IPFS) Copy(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
|
2025-03-27 23:25:23 +08:00
|
|
|
|
if d.Mode != "mfs" {
|
2025-04-12 17:01:30 +08:00
|
|
|
|
return nil, fmt.Errorf("only write in mfs mode")
|
2025-03-27 23:25:23 +08:00
|
|
|
|
}
|
2025-04-12 17:01:30 +08:00
|
|
|
|
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))
|
2023-05-05 17:42:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (d *IPFS) Remove(ctx context.Context, obj model.Obj) error {
|
2025-03-27 23:25:23 +08:00
|
|
|
|
if d.Mode != "mfs" {
|
|
|
|
|
return fmt.Errorf("only write in mfs mode")
|
|
|
|
|
}
|
2023-05-05 17:42:22 +08:00
|
|
|
|
return d.sh.FilesRm(ctx, obj.GetPath(), true)
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-12 17:01:30 +08:00
|
|
|
|
func (d *IPFS) Put(ctx context.Context, dstDir model.Obj, s model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) {
|
2025-03-27 23:25:23 +08:00
|
|
|
|
if d.Mode != "mfs" {
|
2025-04-12 17:01:30 +08:00
|
|
|
|
return nil, fmt.Errorf("only write in mfs mode")
|
2025-03-27 23:25:23 +08:00
|
|
|
|
}
|
|
|
|
|
outHash, err := d.sh.Add(driver.NewLimitedUploadStream(ctx, &driver.ReaderUpdatingProgress{
|
2025-02-16 12:22:11 +08:00
|
|
|
|
Reader: s,
|
|
|
|
|
UpdateProgress: up,
|
2025-03-27 23:25:23 +08:00
|
|
|
|
}))
|
|
|
|
|
if err != nil {
|
2025-04-12 17:01:30 +08:00
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
dstPath := path.Join(dstDir.GetPath(), s.GetName())
|
|
|
|
|
if s.GetExist() != nil {
|
|
|
|
|
d.sh.FilesRm(ctx, dstPath, true)
|
2023-05-05 17:42:22 +08:00
|
|
|
|
}
|
2025-04-12 17:01:30 +08:00
|
|
|
|
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
|
2023-05-05 17:42:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//func (d *Template) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
|
|
|
|
// return nil, errs.NotSupport
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
var _ driver.Driver = (*IPFS)(nil)
|