import Shout import Socket import Keychain import Foundation import DependencyInjection public struct SFTPServiceBackupUploader { public var upload: (URL, @escaping SFTPUploadResult) -> Void public func callAsFunction(url: URL, completion: @escaping SFTPUploadResult) { upload(url, completion) } } extension SFTPServiceBackupUploader { static let mock = SFTPServiceBackupUploader( upload: { url, _ in print("^^^ Requested upload on sftp service") print("^^^ URL path: \(url.path)") } ) static let live = SFTPServiceBackupUploader { url, completion in DispatchQueue.global().async { do { let keychain = try DependencyInjection.Container.shared.resolve() as KeychainHandling let host = try keychain.get(key: .host) let password = try keychain.get(key: .pwd) let username = try keychain.get(key: .username) let ssh = try SSH(host: host!, port: 22) try ssh.authenticate(username: username!, password: password!) let sftp = try ssh.openSftp() let data = try Data(contentsOf: url) if (try? sftp.listFiles(in: "backup")) == nil { try sftp.createDirectory("backup") } try sftp.upload(data: data, remotePath: "backup/backup.xxm") completion(.success(.init( id: "backup/backup.xxm", date: Date(), size: Float(data.count) ))) } catch { if let error = error as? SSHError { print(error.kind) print(error.message) print(error.description) } else if let error = error as? Socket.Error { print(error.errorCode) print(error.description) print(error.errorReason) print(error.localizedDescription) } else { print(error.localizedDescription) } completion(.failure(error)) } } } }