Spaces:
Running
Running
| import { Client } from 'ssh2'; | |
| import fs from 'fs'; | |
| import path from 'path'; | |
| export default class RemoteFetcher { | |
| constructor(config) { | |
| this.config = { | |
| host: config.host, | |
| port: config.port || 22, | |
| username: config.username, | |
| password: config.password, | |
| readyTimeout: 40000, | |
| keepaliveInterval: 10000 | |
| }; | |
| this.conn = new Client(); | |
| } | |
| connect() { | |
| return new Promise((resolve, reject) => { | |
| this.conn.on('ready', () => { | |
| this.conn.sftp((err, sftp) => { | |
| if (err) return reject(err); | |
| this.sftp = sftp; | |
| resolve(); | |
| }); | |
| }) | |
| .on('error', (err) => reject(err)) | |
| .connect(this.config); | |
| }); | |
| } | |
| // Volvemos a fastGet pero con configuración optimizada para velocidad/estabilidad | |
| async fetchFile(remotePath, localPath, onProgress) { | |
| return new Promise((resolve, reject) => { | |
| this.sftp.fastGet(remotePath, localPath, { | |
| concurrency: 6, // 6 descargas en paralelo para mayor velocidad | |
| chunkSize: 32768, // Bloques de 32KB | |
| step: (transferred, chunk, total) => { | |
| if (onProgress) onProgress(transferred, total); | |
| } | |
| }, (err) => { | |
| if (err) return reject(err); | |
| resolve(); | |
| }); | |
| }); | |
| } | |
| async listFiles(remoteDir, extension = '') { | |
| return new Promise((resolve, reject) => { | |
| this.sftp.readdir(remoteDir, (err, list) => { | |
| if (err) return reject(err); | |
| const files = list | |
| .filter(f => f.attrs.isFile()) | |
| .map(f => f.filename); | |
| if (extension) { | |
| resolve(files.filter(f => f.toLowerCase().endsWith(extension.toLowerCase()))); | |
| } else { | |
| resolve(files); | |
| } | |
| }); | |
| }); | |
| } | |
| async listDirectories(remoteDir) { | |
| return new Promise((resolve, reject) => { | |
| this.sftp.readdir(remoteDir, (err, list) => { | |
| if (err) return reject(err); | |
| resolve(list.filter(f => f.attrs.isDirectory() && f.filename !== '.' && f.filename !== '..').map(f => f.filename)); | |
| }); | |
| }); | |
| } | |
| async getFileStats(remotePath) { | |
| return new Promise((resolve, reject) => { | |
| this.sftp.stat(remotePath, (err, stats) => { | |
| if (err) return reject(err); | |
| resolve(stats); | |
| }); | |
| }); | |
| } | |
| disconnect() { | |
| this.conn.end(); | |
| } | |
| } | |