package drive import ( "fmt" "os" "os/exec" "path/filepath" "strings" ) const binaryName = "proton-drive" // Lookup returns the full path to the proton-drive binary in PATH. func Lookup() (string, error) { return exec.LookPath(binaryName) } // CheckAuth verifies that the user is authenticated by listing /my-files. 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 } // Upload uploads localPath to remotePath on Proton Drive. // remotePath is a full path including filename (e.g. /my-files/md/report.md). // The CLI uploads to a parent directory and preserves the local filename, // so if the remote filename differs from the local one, a temp copy is staged. // Existing remote files are overwritten (--conflict-strategy replace). 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) }