package util import ( "fmt" "io" "os" "path/filepath" "strings" "golang.org/x/crypto/ssh" "github.com/pkg/sftp" ) type SftpStorageOpts struct { Host string Port int User string Password string // Logger *slog.Logger } type SftpStorage struct { SftpStorageOpts sshcon *ssh.Client Sftpcon *sftp.Client } func NewSsftp(c SftpStorageOpts) SftpStorage { return SftpStorage{SftpStorageOpts: c} } // caller needs to defer func (s *SftpStorage) Close() { s.Sftpcon.Close() s.sshcon.Close() } func (s *SftpStorage) Connect() (err error) { var auths []ssh.AuthMethod if s.Password != "" { auths = append(auths, ssh.Password(s.Password)) } // Initialize client configuration config := ssh.ClientConfig{ User: s.User, Auth: auths, // ignore host key check HostKeyCallback: ssh.InsecureIgnoreHostKey(), } addr := fmt.Sprintf("%s:%d", s.Host, s.Port) // Connect to server s.sshcon, err = ssh.Dial("tcp", addr, &config) if err != nil { return fmt.Errorf("failed to connecto to [%s]: %v", addr, err) } // Create new SFTP client s.Sftpcon, err = sftp.NewClient(s.sshcon) if err != nil { return fmt.Errorf("unable to start SFTP subsystem [%s]: %v", addr, err) } return } func (s *SftpStorage) UploadFile(localFile, remoteFile string) (err error) { srcFile, err := os.Open(localFile) if err != nil { return fmt.Errorf("unable to open localfile file: %v", err) } defer srcFile.Close() parent := filepath.Dir(remoteFile) path := string(filepath.Separator) dirs := strings.Split(parent, path) for _, dir := range dirs { path = filepath.Join(path, dir) s.Sftpcon.Mkdir(path) } dstFile, err := s.Sftpcon.OpenFile(remoteFile, (os.O_WRONLY | os.O_CREATE | os.O_TRUNC)) if err != nil { return fmt.Errorf("unable to open remote file: %v", err) } defer dstFile.Close() _, err = io.Copy(dstFile, srcFile) if err != nil { return fmt.Errorf("unable to upload file: %v", err) } return }