Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 70 additions & 15 deletions backend/internxt/internxt.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package internxt
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"path"
Expand All @@ -14,11 +15,11 @@ import (
"github.com/StarHack/go-internxt-drive/auth"
"github.com/StarHack/go-internxt-drive/buckets"
config "github.com/StarHack/go-internxt-drive/config"
rclone_config "github.com/rclone/rclone/fs/config"

"github.com/StarHack/go-internxt-drive/files"
"github.com/StarHack/go-internxt-drive/folders"
"github.com/StarHack/go-internxt-drive/users"
"github.com/rclone/rclone/fs"
rclone_config "github.com/rclone/rclone/fs/config"
"github.com/rclone/rclone/fs/config/configmap"
"github.com/rclone/rclone/fs/config/configstruct"
"github.com/rclone/rclone/fs/config/obscure"
Expand Down Expand Up @@ -51,7 +52,7 @@ func init() {
Default: false,
Help: "Simulates empty files by uploading a small placeholder file instead. Alters the filename when uploading to keep track of empty files, but this is not visible through rclone.",
},
{
{
Name: "use_2fa",
Help: "Do you use 2FA to login?",
Default: false,
Expand Down Expand Up @@ -86,7 +87,7 @@ type Options struct {
Password string `flag:"password" help:"Internxt account password"`
Encoding encoder.MultiEncoder `config:"encoding"`
SimulateEmptyFiles bool `config:"simulateEmptyFiles"`
Use2FA bool `config:"use_2fa" help:"Do you use 2FA to login?"`
Use2FA bool `config:"use_2fa" help:"Do you use 2FA to login?"`
}

// Fs represents an Internxt remote
Expand Down Expand Up @@ -191,6 +192,8 @@ func NewFs(ctx context.Context, name, root string, m configmap.Mapper) (fs.Fs, e
DirModTimeUpdatesOnWrite: false,
}).Fill(ctx, f)

// Handle leading and trailing slashes
root = strings.Trim(root, "/")
f.dirCache = dircache.New(root, cfg.RootFolderID, f)

err = f.dirCache.FindRoot(ctx, false)
Expand Down Expand Up @@ -241,24 +244,48 @@ func (f *Fs) Mkdir(ctx context.Context, dir string) error {
}

// Rmdir removes a directory
// Returns an error if it isn't empty
func (f *Fs) Rmdir(ctx context.Context, dir string) error {
return f.purgeCheck(ctx, dir, true)
}

// Purge deletes the directory and all its contents
func (f *Fs) Purge(ctx context.Context, dir string) error {
return f.purgeCheck(ctx, dir, false)
}

func (f *Fs) purgeCheck(ctx context.Context, dir string, check bool) (err error) {
root := path.Join(f.root, dir)
if root == "" {
return errors.New("can't purge root directory")
}

// check that the directory exists
id, err := f.dirCache.FindDir(ctx, dir, false)
if err != nil {
return fs.ErrorDirNotFound
}

// Replace these calls with GetFolderContent? (fmt.Sprintf("/storage/v2/folder/%d%s", folderID, query))
childFolders, err := folders.ListAllFolders(f.cfg, id)
if err != nil {
return err
}
childFiles, err := folders.ListAllFiles(f.cfg, id)
if err != nil {
return err
}
if check {
// Replace these calls with GetFolderContent? (fmt.Sprintf("/storage/v2/folder/%d%s", folderID, query))
// Check folders and files separately in case we only need to call the API once.
childFolders, err := folders.ListAllFolders(f.cfg, id)
if err != nil {
return err
}

if len(childFolders) > 0 {
return fs.ErrorDirectoryNotEmpty
}

if len(childFiles) > 0 || len(childFolders) > 0 {
return fs.ErrorDirectoryNotEmpty
childFiles, err := folders.ListAllFiles(f.cfg, id)
if err != nil {
return err
}

if len(childFiles) > 0 {
return fs.ErrorDirectoryNotEmpty
}
}

err = folders.DeleteFolder(f.cfg, id)
Expand All @@ -272,6 +299,7 @@ func (f *Fs) Rmdir(ctx context.Context, dir string) error {
f.dirCache.FlushDir(dir)
time.Sleep(500 * time.Millisecond) // REMOVE THIS, use pacer to check for consistency?
return nil

}

// FindLeaf looks for a sub‑folder named `leaf` under the Internxt folder `pathID`.
Expand Down Expand Up @@ -587,6 +615,28 @@ func (o *Object) SetModTime(ctx context.Context, t time.Time) error {
return fs.ErrorCantSetModTime
}

// About gets quota information
func (f *Fs) About(ctx context.Context) (*fs.Usage, error) {
internxtLimit, err := users.GetLimit(f.cfg)
if err != nil {
return nil, err
}

internxtUsage, err := users.GetUsage(f.cfg)
if err != nil {
return nil, err
}

usage := &fs.Usage{
Used: fs.NewUsageValue(internxtUsage.Drive),
}

usage.Total = fs.NewUsageValue(internxtLimit.MaxSpaceBytes)
usage.Free = fs.NewUsageValue(*usage.Total - *usage.Used)

return usage, nil
}

// Open opens a file for streaming
func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadCloser, error) {
fs.FixRangeOption(options, o.size)
Expand Down Expand Up @@ -623,6 +673,11 @@ func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, op
}
o.remote = o.remote + EMPTY_FILE_EXT
}
} else {
if o.f.opt.SimulateEmptyFiles {
// Remove the suffix if we're updating an empty file with actual data
o.remote = strings.TrimSuffix(o.remote, EMPTY_FILE_EXT)
}
}

// Check if object exists on the server
Expand Down