You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
163 lines
3.0 KiB
Go
163 lines
3.0 KiB
Go
2 years ago
|
package storage
|
||
2 years ago
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"io"
|
||
2 years ago
|
"log"
|
||
|
"strings"
|
||
2 years ago
|
"time"
|
||
|
|
||
|
ipfsapi "github.com/ipfs/go-ipfs-api"
|
||
|
"golang.org/x/net/context"
|
||
|
)
|
||
|
|
||
|
func NewIPFSFilestore(ipfs *ipfsapi.Shell, rootDir string) (*IPFSFilestore, error) {
|
||
|
store := &IPFSFilestore{
|
||
|
ipfs: ipfs,
|
||
|
rootDir: rootDir,
|
||
|
}
|
||
|
|
||
|
exists, err := store.Exists("")
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if !exists {
|
||
|
err := ipfs.FilesMkdir(newContext(), rootDir)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return store, nil
|
||
|
}
|
||
|
|
||
|
type IPFSFilestore struct {
|
||
|
ipfs *ipfsapi.Shell
|
||
|
rootDir string
|
||
|
}
|
||
|
|
||
|
var ErrFileNotFound = errors.New("File not found")
|
||
|
|
||
|
type ErrFailedFileExistsCheck struct {
|
||
|
parent error
|
||
|
}
|
||
|
|
||
|
func (e *ErrFailedFileExistsCheck) Unwrap() error {
|
||
|
return e.parent
|
||
|
}
|
||
|
|
||
|
func (e *ErrFailedFileExistsCheck) Error() string {
|
||
|
msg := "Failed to determine whether file exists"
|
||
|
|
||
|
if e.parent != nil {
|
||
|
msg += ": " + e.parent.Error()
|
||
|
}
|
||
|
|
||
|
return msg
|
||
|
}
|
||
|
|
||
|
func newContext() context.Context {
|
||
|
ctx, _ := context.WithTimeout(context.Background(), 10*time.Minute)
|
||
|
return ctx
|
||
|
}
|
||
|
|
||
|
func (s *IPFSFilestore) Exists(name string) (bool, error) {
|
||
|
ctx := newContext()
|
||
2 years ago
|
_, err := s.ipfs.FilesStat(ctx, s.filepath(name))
|
||
|
if err != nil {
|
||
|
if err.Error() == "files/stat: file does not exist" {
|
||
|
return false, nil
|
||
|
}
|
||
|
|
||
|
return false, &ErrFailedFileExistsCheck{parent: err}
|
||
|
}
|
||
|
|
||
|
return true, nil
|
||
|
}
|
||
|
|
||
|
func (s *IPFSFilestore) DirectoryExists(name string) (bool, error) {
|
||
|
ctx := newContext()
|
||
|
st, err := s.ipfs.FilesStat(ctx, s.filepath(name))
|
||
2 years ago
|
if err != nil {
|
||
|
if err.Error() == "files/stat: file does not exist" {
|
||
|
return false, nil
|
||
|
}
|
||
|
|
||
|
return false, &ErrFailedFileExistsCheck{parent: err}
|
||
|
}
|
||
|
|
||
2 years ago
|
if st.Type != "directory" {
|
||
|
return false, nil
|
||
|
}
|
||
|
|
||
2 years ago
|
return true, nil
|
||
|
}
|
||
|
|
||
2 years ago
|
func (s *IPFSFilestore) filepath(name string) string {
|
||
|
return s.rootDir + "/" + strings.TrimPrefix(name, "/")
|
||
|
}
|
||
|
|
||
|
func (s *IPFSFilestore) MakeDirectory(name string) error {
|
||
|
filepath := s.filepath(name)
|
||
|
log.Printf("Making directory \"%s\"", filepath)
|
||
|
return s.ipfs.FilesMkdir(newContext(), filepath)
|
||
|
}
|
||
|
|
||
2 years ago
|
type FileReader struct {
|
||
|
ipfs *ipfsapi.Shell
|
||
|
file *File
|
||
|
reader io.ReadCloser
|
||
|
}
|
||
|
|
||
|
func (r *FileReader) Read(p []byte) (int, error) {
|
||
|
if r.reader == nil {
|
||
|
var err error
|
||
|
r.reader, err = r.ipfs.FilesRead(newContext(), r.file.AbsolutePath)
|
||
|
if err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return r.reader.Read(p)
|
||
|
}
|
||
|
|
||
|
func (r *FileReader) Close() error {
|
||
|
if r.reader == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
return r.reader.Close()
|
||
|
}
|
||
|
|
||
|
func (s *IPFSFilestore) Get(name string) (*File, error) {
|
||
|
exists, err := s.Exists(name)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if !exists {
|
||
|
return nil, ErrFileNotFound
|
||
|
}
|
||
|
|
||
|
file := &File{
|
||
|
Name: name,
|
||
2 years ago
|
AbsolutePath: s.filepath(name),
|
||
2 years ago
|
}
|
||
|
|
||
|
file.Contents = &FileReader{
|
||
|
ipfs: s.ipfs,
|
||
|
file: file,
|
||
|
}
|
||
|
|
||
|
return file, nil
|
||
|
}
|
||
|
|
||
|
func (s *IPFSFilestore) Set(file *File) error {
|
||
2 years ago
|
return s.ipfs.FilesWrite(newContext(), s.filepath(file.Name), file.Contents)
|
||
2 years ago
|
}
|
||
|
|
||
|
func (s *IPFSFilestore) Delete(file *File) error {
|
||
|
return s.ipfs.FilesRm(newContext(), file.AbsolutePath, true)
|
||
|
}
|