universal-file-client
Version:
Universal file transfer client with unified interface for FTP, SFTP, and HTTP protocols
234 lines • 8.54 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.UniversalFileClient = void 0;
const ftp_adapter_1 = require("./adapters/ftp-adapter");
const sftp_adapter_1 = require("./adapters/sftp-adapter");
const http_adapter_1 = require("./adapters/http-adapter");
const protocol_detector_1 = require("./utils/protocol-detector");
const file_matcher_1 = require("./utils/file-matcher");
const path = __importStar(require("path"));
class UniversalFileClient {
constructor() {
this.adapter = null;
this.protocol = null;
this.connectionConfig = null;
}
async connect(config) {
if (!config.host) {
throw new Error('Host is required for connection');
}
const protocol = protocol_detector_1.ProtocolDetector.detect(config.host);
this.protocol = protocol;
this.connectionConfig = config;
// Create appropriate adapter
switch (protocol) {
case 'ftp':
case 'ftps':
this.adapter = new ftp_adapter_1.FtpAdapter();
break;
case 'sftp':
this.adapter = new sftp_adapter_1.SftpAdapter();
break;
case 'http':
case 'https':
this.adapter = new http_adapter_1.HttpAdapter();
break;
default:
throw new Error(`Unsupported protocol: ${protocol}`);
}
await this.adapter.connect(config);
}
async disconnect() {
if (this.adapter) {
await this.adapter.disconnect();
this.adapter = null;
this.protocol = null;
this.connectionConfig = null;
}
}
async list(pathArg = '.', options = {}) {
if (!this.adapter) {
throw new Error('Not connected to any server');
}
try {
const files = await this.adapter.list(pathArg);
if (!options.includeDirectories) {
return files.filter(file => !file.isDirectory);
}
return files;
}
catch (error) {
throw new Error(`Failed to list directory: ${error.message}`);
}
}
async download(remotePath, options = {}) {
if (!this.adapter) {
throw new Error('Not connected to any server');
}
const retries = options.retries || 3;
const retryDelay = options.retryDelay || 1000;
for (let attempt = 0; attempt <= retries; attempt++) {
try {
return await this.adapter.download(remotePath);
}
catch (error) {
if (attempt === retries) {
throw error;
}
await this.sleep(retryDelay * Math.pow(2, attempt));
}
}
throw new Error('Download failed after all retries');
}
async upload(localPath, remotePath) {
if (!this.adapter) {
throw new Error('Not connected to any server');
}
if (!localPath || !remotePath) {
throw new Error('Both local and remote paths are required for upload');
}
try {
return await this.adapter.upload(localPath, remotePath);
}
catch (error) {
throw new Error(`Failed to upload file: ${error.message}`);
}
}
async stat(filePath) {
if (!this.adapter) {
throw new Error('Not connected to any server');
}
if (!filePath) {
throw new Error('File path is required for stat operation');
}
try {
return await this.adapter.stat(filePath);
}
catch (error) {
throw new Error(`Failed to get file stats: ${error.message}`);
}
}
async exists(filePath) {
if (!this.adapter) {
throw new Error('Not connected to any server');
}
if (!filePath) {
throw new Error('File path is required for exists check');
}
try {
return await this.adapter.exists(filePath);
}
catch (error) {
// Return false for exists check instead of throwing
return false;
}
}
async lastModified(filePath) {
if (!this.adapter) {
throw new Error('Not connected to any server');
}
return this.adapter.lastModified(filePath);
}
async findFile(targetPath, fileNameType = 'smart') {
if (!this.adapter) {
throw new Error('Not connected to any server');
}
const dirPath = path.dirname(targetPath);
const parsedPath = path.parse(targetPath);
const { name: basename, ext: extname } = parsedPath;
try {
// First try exact path
const exactFile = await this.stat(targetPath);
if (exactFile) {
return { file: exactFile, actualPath: targetPath };
}
// List directory and find matching files
const files = await this.list(dirPath);
const matchOptions = {
basename,
filepath: targetPath,
extname,
};
const matchedFile = file_matcher_1.FileMatcher.findBestMatch(files, matchOptions, fileNameType);
if (matchedFile) {
const actualPath = path.join(dirPath, matchedFile.name);
return { file: matchedFile, actualPath };
}
return null;
}
catch (error) {
throw new Error(`Failed to find file: ${error.message}`);
}
}
async checkForUpdates(filePath, lastKnownDate, fileNameType = 'smart') {
const result = await this.findFile(filePath, fileNameType);
if (!result) {
return { hasUpdate: false };
}
const { file, actualPath } = result;
// For HTTP/JSON files, always consider as updated (API exception)
if (this.protocol === 'http' || this.protocol === 'https') {
const httpAdapter = this.adapter;
const fileInfo = await httpAdapter.getFileInfo(actualPath);
if (fileInfo.isJson) {
return { hasUpdate: true, file, actualPath };
}
// For non-JSON HTTP files, check last modified
if (!fileInfo.lastModified) {
return { hasUpdate: false, file, actualPath };
}
const hasUpdate = fileInfo.lastModified.getTime() > lastKnownDate.getTime();
return { hasUpdate, file, actualPath };
}
// For FTP/SFTP, check modification time
const hasUpdate = file.date.getTime() > lastKnownDate.getTime();
return { hasUpdate, file, actualPath };
}
getProtocol() {
return this.protocol;
}
getConnectionConfig() {
return this.connectionConfig;
}
isConnected() {
return this.adapter !== null;
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
exports.UniversalFileClient = UniversalFileClient;
//# sourceMappingURL=universal-ftp-client.js.map