package storage import ( "errors" "io" "log" "strings" "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() _, 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)) if err != nil { if err.Error() == "files/stat: file does not exist" { return false, nil } return false, &ErrFailedFileExistsCheck{parent: err} } if st.Type != "directory" { return false, nil } return true, nil } 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) } 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, AbsolutePath: s.filepath(name), } file.Contents = &FileReader{ ipfs: s.ipfs, file: file, } return file, nil } func (s *IPFSFilestore) Set(file *File) error { return s.ipfs.FilesWrite(newContext(), s.filepath(file.Name), file.Contents) } func (s *IPFSFilestore) Delete(file *File) error { return s.ipfs.FilesRm(newContext(), file.AbsolutePath, true) }