package drive import ( "fmt" "os" "os/exec" "path/filepath" "strings" ) const binaryName = "proton-drive" func Lookup() (string, error) { return exec.LookPath(binaryName) } func CheckAuth() error { binary, err := Lookup() if err != nil { return fmt.Errorf("proton-drive not found in PATH: %w", err) } cmd := exec.Command(binary, "filesystem", "list", "/my-files") cmd.Stdout = nil cmd.Stderr = nil if err := cmd.Run(); err != nil { return fmt.Errorf("not authenticated with proton-drive (run 'proton-drive auth login'): %w", err) } return nil } // EnsureRemoteParent creates the parent remote directory if it doesn't exist. func EnsureRemoteParent(remoteDir string) error { binary, err := Lookup() if err != nil { return fmt.Errorf("proton-drive not found in PATH: %w", err) } parentDir := filepath.Dir(remoteDir) // Try create-folder on the grandparent for the parent grandparent := filepath.Dir(parentDir) parentName := filepath.Base(parentDir) cmd := exec.Command(binary, "filesystem", "create-folder", grandparent, parentName) cmd.Stdout = nil cmd.Stderr = nil cmd.Run() // Ignore errors — folder may already exist return nil } // UploadDir uploads an entire local directory to Proton Drive. // The local directory's basename becomes the remote directory name. func UploadDir(localDir, remoteDir string) error { binary, err := Lookup() if err != nil { return fmt.Errorf("proton-drive not found in PATH: %w", err) } // Ensure parent of remote dir exists if err := EnsureRemoteParent(remoteDir); err != nil { return fmt.Errorf("cannot prepare remote directory: %w", err) } // Upload to parent — the local dir's basename creates the remote dir remoteParent := filepath.Dir(remoteDir) cmd := exec.Command(binary, "filesystem", "upload", localDir, remoteParent, "--conflict-strategy", "replace") var stderr strings.Builder cmd.Stderr = &stderr if err := cmd.Run(); err != nil { return fmt.Errorf("upload to proton-drive failed: %w: %s", err, strings.TrimSpace(stderr.String())) } return nil } // Upload uploads a single local file to a remote path on Proton Drive. func Upload(localPath, remotePath string) error { binary, err := Lookup() if err != nil { return fmt.Errorf("proton-drive not found in PATH: %w", err) } remoteDir := filepath.Dir(remotePath) remoteName := filepath.Base(remotePath) localName := filepath.Base(localPath) if remoteName == localName { return runUpload(binary, localPath, remoteDir) } tmpDir, err := os.MkdirTemp("", "dirmd-upload-*") if err != nil { return fmt.Errorf("cannot create temp dir: %w", err) } defer os.RemoveAll(tmpDir) tmpFile := filepath.Join(tmpDir, remoteName) if err := copyFile(localPath, tmpFile); err != nil { return fmt.Errorf("cannot stage temp file: %w", err) } return runUpload(binary, tmpFile, remoteDir) } func runUpload(binary, localPath, remoteDir string) error { cmd := exec.Command(binary, "filesystem", "upload", localPath, remoteDir, "--conflict-strategy", "replace") var stderr strings.Builder cmd.Stderr = &stderr if err := cmd.Run(); err != nil { return fmt.Errorf("upload to proton-drive failed: %w: %s", err, strings.TrimSpace(stderr.String())) } return nil } func copyFile(src, dst string) error { data, err := os.ReadFile(src) if err != nil { return err } return os.WriteFile(dst, data, 0644) }