UNPKG

@kadi.build/local-remote-file-manager-ability

Version:

Local & Remote File Management System with S3-compatible container registry, HTTP server provider, file streaming, and comprehensive testing suite

494 lines (407 loc) 16.9 kB
import { LocalProvider } from './providers/localProvider.js'; import { WatchProvider } from './providers/watchProvider.js'; import { CompressionProvider } from './providers/compressionProvider.js'; import { TunnelProvider } from './providers/tunnelProvider.js'; import EventEmitter from 'events'; import path from 'path'; import os from 'os'; class LocalRemoteManager extends EventEmitter { constructor(config) { super(); this.config = config; this.providers = {}; this.initializeProviders(); // Set up event handling after providers are initialized process.nextTick(() => { this.setupEventHandling(); }); } initializeProviders() { // Initialize Local Provider (always available) this.providers.local = new LocalProvider(this.config.getLocalConfig()); // Initialize Watch Provider (Bucket 2) this.providers.watch = new WatchProvider(this.config.getWatchConfig()); // Initialize Compression Provider (Bucket 3) this.providers.compression = new CompressionProvider(this.config.getCompressionConfig()); // Initialize Tunnel Provider (Bucket 4) this.providers.tunnel = new TunnelProvider(this.config.getTunnelConfig()); // Future buckets will add more providers: // this.providers.remote = new RemoteProvider(this.config.getRemoteConfig()); } getProvider(providerName = 'local') { const provider = this.providers[providerName.toLowerCase()]; if (!provider) { throw new Error(`Provider '${providerName}' not available. Available providers: ${Object.keys(this.providers).join(', ')}`); } return provider; } // Convenience method to get compression provider getCompressionProvider() { return this.getProvider('compression'); } // ============================================================================ // FILE OPERATIONS (mirroring CloudStorageManager) // ============================================================================ async uploadFile(sourcePath, targetPath, providerName = 'local') { const provider = this.getProvider(providerName); return await provider.uploadFile(sourcePath, targetPath); } async downloadFile(sourcePath, targetPath, providerName = 'local') { const provider = this.getProvider(providerName); return await provider.downloadFile(sourcePath, targetPath); } async getFileInfo(filePath, providerName = 'local') { const provider = this.getProvider(providerName); return await provider.getFile(filePath); } async listFiles(directoryPath = '/', providerName = 'local', options = {}) { const provider = this.getProvider(providerName); return await provider.listFiles(directoryPath, options); } async deleteFile(filePath, providerName = 'local') { const provider = this.getProvider(providerName); return await provider.deleteFile(filePath); } async renameFile(oldPath, newName, providerName = 'local') { const provider = this.getProvider(providerName); return await provider.renameFile(oldPath, newName); } async copyFile(sourcePath, targetPath, providerName = 'local') { const provider = this.getProvider(providerName); return await provider.copyFile(sourcePath, targetPath); } async moveFile(sourcePath, targetPath, providerName = 'local') { const provider = this.getProvider(providerName); return await provider.moveFile(sourcePath, targetPath); } // ============================================================================ // FOLDER OPERATIONS (mirroring CloudStorageManager) // ============================================================================ async createFolder(folderPath, providerName = 'local') { const provider = this.getProvider(providerName); return await provider.createFolder(folderPath); } async listFolders(directoryPath = '/', providerName = 'local') { const provider = this.getProvider(providerName); return await provider.listFolders(directoryPath); } async deleteFolder(folderPath, recursive = false, providerName = 'local') { const provider = this.getProvider(providerName); return await provider.deleteFolder(folderPath, recursive); } async renameFolder(oldPath, newName, providerName = 'local') { const provider = this.getProvider(providerName); return await provider.renameFolder(oldPath, newName); } async getFolderInfo(folderPath, providerName = 'local') { const provider = this.getProvider(providerName); return await provider.getFolder(folderPath); } async copyFolder(sourcePath, targetPath, providerName = 'local') { const provider = this.getProvider(providerName); return await provider.copyFolder(sourcePath, targetPath); } async moveFolder(sourcePath, targetPath, providerName = 'local') { const provider = this.getProvider(providerName); return await provider.moveFolder(sourcePath, targetPath); } // ============================================================================ // SEARCH OPERATIONS (mirroring CloudStorageManager) // ============================================================================ async searchFiles(query, providerName = 'local', options = {}) { const provider = this.getProvider(providerName); return await provider.searchFiles(query, options); } // ============================================================================ // UTILITY OPERATIONS (mirroring CloudStorageManager) // ============================================================================ async testConnection(providerName = 'local') { const provider = this.getProvider(providerName); return await provider.testConnection(); } async validateProvider(providerName = 'local') { const provider = this.getProvider(providerName); if (provider.validateConfig) { return await provider.validateConfig(); } return { isValid: true, errors: [], warnings: [] }; } getAvailableProviders() { return Object.keys(this.providers); } // ============================================================================ // BATCH OPERATIONS (for future Bucket 5) // ============================================================================ async uploadMultipleFiles(fileList, targetDirectory = '/', providerName = 'local') { const results = []; const errors = []; for (const filePath of fileList) { try { const fileName = path.basename(filePath); const targetPath = path.join(targetDirectory, fileName); const result = await this.uploadFile(filePath, targetPath, providerName); results.push({ sourcePath: filePath, targetPath, result }); } catch (error) { errors.push({ filePath, error: error.message }); } } return { results, errors }; } async downloadMultipleFiles(fileList, targetDirectory = './', providerName = 'local') { const results = []; const errors = []; for (const filePath of fileList) { try { const fileName = path.basename(filePath); const targetPath = path.join(targetDirectory, fileName); const result = await this.downloadFile(filePath, targetPath, providerName); results.push({ sourcePath: filePath, targetPath, result }); } catch (error) { errors.push({ filePath, error: error.message }); } } return { results, errors }; } // ============================================================================ // FILE WATCHING METHODS (Bucket 2) // ============================================================================ async startWatching(directoryPath, options = {}) { const watchProvider = this.getProvider('watch'); return await watchProvider.startWatching(directoryPath, options); } async stopWatching(watchIdOrPath) { const watchProvider = this.getProvider('watch'); return await watchProvider.stopWatching(watchIdOrPath); } async stopAllWatching() { const watchProvider = this.getProvider('watch'); return await watchProvider.stopAllWatching(); } listActiveWatchers() { const watchProvider = this.getProvider('watch'); return watchProvider.listActiveWatchers(); } getWatcherInfo(watchIdOrPath) { const watchProvider = this.getProvider('watch'); return watchProvider.getWatcherInfo(watchIdOrPath); } getWatchingStatus() { const watchProvider = this.getProvider('watch'); return watchProvider.getWatchingStatus(); } // ============================================================================ // COMPRESSION METHODS (Bucket 3) // ============================================================================ async compressFile(inputPath, outputPath, options = {}) { const compressionProvider = this.getProvider('compression'); return await compressionProvider.compressFile(inputPath, outputPath, options); } async decompressFile(archivePath, outputDirectory, options = {}) { const compressionProvider = this.getProvider('compression'); return await compressionProvider.decompressFile(archivePath, outputDirectory, options); } async compressMultipleFiles(fileList, outputDirectory, options = {}) { const compressionProvider = this.getProvider('compression'); return await compressionProvider.compressMultipleFiles(fileList, outputDirectory, options); } async decompressMultipleFiles(archiveList, outputDirectory, options = {}) { const compressionProvider = this.getProvider('compression'); return await compressionProvider.decompressMultipleFiles(archiveList, outputDirectory, options); } listActiveCompressionOperations() { const compressionProvider = this.getProvider('compression'); return compressionProvider.listActiveOperations(); } getCompressionOperationInfo(operationId) { const compressionProvider = this.getProvider('compression'); return compressionProvider.getOperationInfo(operationId); } getCompressionStatus() { const compressionProvider = this.getProvider('compression'); return compressionProvider.getCompressionStatus(); } // ============================================================================ // FUTURE BUCKET METHODS (stubs for now) // ============================================================================ // Bucket 4: Tunneling async createTunnel(options = {}) { const provider = this.getProvider('tunnel'); return await provider.createTunnel(options); } async destroyTunnel(tunnelId) { const provider = this.getProvider('tunnel'); return await provider.destroyTunnel(tunnelId); } async createTemporaryUrl(filePath, options = {}) { const provider = this.getProvider('tunnel'); return await provider.createTemporaryUrl(filePath, options); } async revokeTemporaryUrl(urlId) { const provider = this.getProvider('tunnel'); return await provider.revokeTemporaryUrl(urlId); } getTunnelStatus() { const provider = this.getProvider('tunnel'); return provider.getTunnelStatus(); } listActiveTunnels() { const provider = this.getProvider('tunnel'); return provider.listActiveTunnels(); } listActiveUrls() { const provider = this.getProvider('tunnel'); return provider.listActiveUrls(); } // Convenience method to get tunnel provider getTunnelProvider() { return this.getProvider('tunnel'); } // ============================================================================ // EVENT HANDLING SETUP // ============================================================================ setupEventHandling() { try { // Forward tunnel events const tunnelProvider = this.getProvider('tunnel'); tunnelProvider.on('tunnelProgress', (progressData) => { this.emit('tunnelProgress', progressData); }); tunnelProvider.on('tunnelError', (errorData) => { this.emit('tunnelError', errorData); }); tunnelProvider.on('urlCreated', (urlData) => { this.emit('urlCreated', urlData); }); tunnelProvider.on('urlRevoked', (revokeData) => { this.emit('urlRevoked', revokeData); }); tunnelProvider.on('fileAccessed', (accessData) => { this.emit('fileAccessed', accessData); }); } catch (error) { console.warn('⚠️ Tunnel provider not available for event handling'); } try { // Forward file watching events const watchProvider = this.getProvider('watch'); watchProvider.on('fileEvent', (eventData) => { this.emit('fileEvent', eventData); }); watchProvider.on('watcherError', (errorData) => { this.emit('watcherError', errorData); }); } catch (error) { // Watch provider might not be available, ignore silently console.warn('⚠️ Watch provider not available for event handling'); } try { // Forward compression events const compressionProvider = this.getProvider('compression'); compressionProvider.on('compressionProgress', (progressData) => { this.emit('compressionProgress', progressData); }); compressionProvider.on('decompressionProgress', (progressData) => { this.emit('decompressionProgress', progressData); }); } catch (error) { // Compression provider might not be available, ignore silently console.warn('⚠️ Compression provider not available for event handling'); } } // ============================================================================ // UTILITY METHODS // ============================================================================ formatBytes(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } getSystemInfo() { return { platform: os.platform(), arch: os.arch(), nodeVersion: process.version, availableProviders: this.getAvailableProviders(), config: { local: this.config.getLocalConfig(), performance: this.config.getPerformanceConfig(), watch: this.config.getWatchConfig(), compression: this.config.getCompressionConfig() } }; } async getUsageStats() { const provider = this.getProvider('local'); const localConfig = this.config.getLocalConfig(); try { const fs = (await import('fs')).promises; const path = await import('path'); const stats = { uploadDirectory: { path: localConfig.uploadDirectory, exists: false, fileCount: 0, totalSize: 0 }, downloadDirectory: { path: localConfig.downloadDirectory, exists: false, fileCount: 0, totalSize: 0 }, tempDirectory: { path: localConfig.tempDirectory, exists: false, fileCount: 0, totalSize: 0 }, watchStatus: this.getWatchingStatus(), compressionStatus: this.getCompressionStatus() }; // Check each directory for (const [key, dirInfo] of Object.entries(stats)) { if (key === 'watchStatus' || key === 'compressionStatus') continue; // Skip status objects try { const dirStat = await fs.stat(dirInfo.path); if (dirStat.isDirectory()) { dirInfo.exists = true; const files = await provider.listFiles(dirInfo.path, { recursive: true }); dirInfo.fileCount = files.length; dirInfo.totalSize = files.reduce((sum, file) => sum + (file.size || 0), 0); } } catch (error) { // Directory doesn't exist or can't be accessed dirInfo.exists = false; } } return stats; } catch (error) { throw new Error(`Failed to get usage stats: ${error.message}`); } } // ============================================================================ // CLEANUP AND SHUTDOWN // ============================================================================ async shutdown() { console.log('🔄 Shutting down Local Remote Manager...'); try { // Stop all watchers first const watchProvider = this.getProvider('watch'); await watchProvider.shutdown(); // Shutdown compression provider const compressionProvider = this.getProvider('compression'); await compressionProvider.shutdown(); // Shutdown tunnel provider const tunnelProvider = this.getProvider('tunnel'); await tunnelProvider.shutdown(); // Future: shutdown other providers console.log('✅ Local Remote Manager shutdown complete'); } catch (error) { console.error('❌ Error during shutdown:', error.message); throw error; } } } export { LocalRemoteManager };