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

295 lines (241 loc) โ€ข 9.86 kB
// Tunnel Diagnostic Tool // Create this as a new file: src/utils/tunnelDiagnostic.js import { execSync } from 'child_process'; import http from 'http'; import https from 'https'; import { promises as fs } from 'fs'; import { LocalRemoteManager } from '../localRemoteManager.js'; import { ConfigManager } from '../configManager.js'; class TunnelDiagnostic { constructor() { this.results = { localServer: false, tunnelCreation: false, urlGeneration: false, fileServing: false, errors: [] }; } async runDiagnostic(filePath) { console.log('๐Ÿ” Running Tunnel Diagnostic'); console.log('============================\n'); try { await this.testLocalServer(); await this.testTunnelCreation(); await this.testUrlGeneration(filePath); await this.testFileServing(filePath); this.displayResults(); } catch (error) { console.error(`โŒ Diagnostic failed: ${error.message}`); this.results.errors.push(error.message); this.displayResults(); } } async testLocalServer() { console.log('๐Ÿงช Testing Local Server...'); try { const config = new ConfigManager(); await config.load(); const manager = new LocalRemoteManager(config); // Get tunnel provider and start server const tunnelProvider = manager.providers.tunnel; const port = await tunnelProvider.startLocalServer(); console.log(` โœ… Local server started on port ${port}`); // Test server responsiveness const response = await this.makeHttpRequest(`http://localhost:${port}/health`); if (response.statusCode === 200) { console.log(' โœ… Server health check passed'); this.results.localServer = true; } else { throw new Error(`Health check failed: ${response.statusCode}`); } // Clean up await tunnelProvider.stopLocalServer(); } catch (error) { console.log(` โŒ Local server test failed: ${error.message}`); this.results.errors.push(`Local Server: ${error.message}`); } } async testTunnelCreation() { console.log('\n๐Ÿงช Testing Tunnel Creation...'); try { const config = new ConfigManager(); await config.load(); const manager = new LocalRemoteManager(config); console.log(' ๐Ÿ”— Creating tunnel...'); const tunnel = await manager.createTunnel(); console.log(` โœ… Tunnel created: ${tunnel.url}`); console.log(` โœ… Service: ${tunnel.service}`); // Test tunnel responsiveness console.log(' ๐Ÿงช Testing tunnel connectivity...'); try { const response = await this.makeHttpRequest(tunnel.url, 10000); console.log(` ๐Ÿ“Š Tunnel response: ${response.statusCode}`); console.log(` ๐Ÿ“„ Response length: ${response.body.length} chars`); console.log(` ๐Ÿ“ Response preview: ${response.body.substring(0, 100)}...`); if (response.statusCode === 200) { this.results.tunnelCreation = true; } } catch (tunnelError) { console.log(` โš ๏ธ Tunnel connectivity issue: ${tunnelError.message}`); } // Clean up await manager.destroyTunnel(tunnel.tunnelId); } catch (error) { console.log(` โŒ Tunnel creation failed: ${error.message}`); this.results.errors.push(`Tunnel Creation: ${error.message}`); } } async testUrlGeneration(filePath) { console.log('\n๐Ÿงช Testing URL Generation...'); try { const config = new ConfigManager(); await config.load(); const manager = new LocalRemoteManager(config); // Verify file exists const stats = await fs.stat(filePath); console.log(` ๐Ÿ“„ File: ${filePath} (${stats.size} bytes)`); console.log(' ๐Ÿ”— Creating shareable URL...'); const result = await manager.createTemporaryUrl(filePath, { expiresAt: new Date(Date.now() + 300000) // 5 minutes }); console.log(` โœ… URL created: ${result.shareableUrl}`); console.log(` ๐Ÿ†” URL ID: ${result.urlId}`); console.log(` ๐Ÿ”‘ Token: ${result.accessToken.substring(0, 16)}...`); // Parse URL to understand structure const url = new URL(result.shareableUrl); console.log(` ๐Ÿ  Base URL: ${url.origin}`); console.log(` ๐Ÿ“ Path: ${url.pathname}`); console.log(` ๐Ÿ”— Query: ${url.search}`); this.results.urlGeneration = true; this.generatedUrl = result.shareableUrl; this.urlId = result.urlId; // Don't clean up yet - we'll use this for file serving test } catch (error) { console.log(` โŒ URL generation failed: ${error.message}`); this.results.errors.push(`URL Generation: ${error.message}`); } } async testFileServing(filePath) { console.log('\n๐Ÿงช Testing File Serving...'); if (!this.generatedUrl) { console.log(' โญ๏ธ Skipped: No URL to test'); return; } try { const config = new ConfigManager(); await config.load(); const manager = new LocalRemoteManager(config); console.log(` ๐ŸŒ Testing URL: ${this.generatedUrl}`); // Test the actual generated URL const response = await this.makeHttpRequest(this.generatedUrl, 15000); console.log(` ๐Ÿ“Š Response status: ${response.statusCode}`); console.log(` ๐Ÿ“ฆ Content length: ${response.body.length} bytes`); console.log(` ๐ŸŽญ Content type: ${response.headers['content-type'] || 'unknown'}`); if (response.body.length > 0) { console.log(` ๐Ÿ“ Content preview: ${response.body.substring(0, 200)}...`); // Check if it's the expected file content const expectedContent = await fs.readFile(filePath, 'utf8'); if (response.body === expectedContent) { console.log(' โœ… File content matches exactly!'); this.results.fileServing = true; } else if (response.body.includes('<!DOCTYPE html>')) { console.log(' โŒ Received HTML instead of file content (tunnel status page)'); } else if (response.body.length === 0) { console.log(' โŒ Received empty content'); } else { console.log(' โš ๏ธ Content differs from expected file'); console.log(` ๐Ÿ“ Expected: ${expectedContent.length} chars, Got: ${response.body.length} chars`); } } else { console.log(' โŒ Empty response body'); } } catch (error) { console.log(` โŒ File serving test failed: ${error.message}`); this.results.errors.push(`File Serving: ${error.message}`); } } async makeHttpRequest(url, timeout = 5000) { return new Promise((resolve, reject) => { const parsedUrl = new URL(url); const options = { hostname: parsedUrl.hostname, port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80), path: parsedUrl.pathname + parsedUrl.search, method: 'GET', timeout: timeout, headers: { 'User-Agent': 'TunnelDiagnostic/1.0' } }; const client = parsedUrl.protocol === 'https:' ? https : http; const req = client.request(options, (res) => { let body = ''; res.on('data', chunk => body += chunk); res.on('end', () => { resolve({ statusCode: res.statusCode, headers: res.headers, body: body }); }); }); req.on('error', reject); req.on('timeout', () => { req.destroy(); reject(new Error('Request timeout')); }); req.setTimeout(timeout); req.end(); }); } displayResults() { console.log('\n' + '='.repeat(50)); console.log('๐Ÿ” DIAGNOSTIC RESULTS'); console.log('='.repeat(50)); console.log(`Local Server: ${this.results.localServer ? 'โœ…' : 'โŒ'}`); console.log(`Tunnel Creation: ${this.results.tunnelCreation ? 'โœ…' : 'โŒ'}`); console.log(`URL Generation: ${this.results.urlGeneration ? 'โœ…' : 'โŒ'}`); console.log(`File Serving: ${this.results.fileServing ? 'โœ…' : 'โŒ'}`); if (this.results.errors.length > 0) { console.log('\nโŒ ERRORS:'); this.results.errors.forEach(error => { console.log(` โ€ข ${error}`); }); } const allPassed = this.results.localServer && this.results.tunnelCreation && this.results.urlGeneration && this.results.fileServing; if (allPassed) { console.log('\n๐ŸŽ‰ ALL TESTS PASSED! The tunneling system should work correctly.'); } else { console.log('\n๐Ÿšจ SOME TESTS FAILED. Please address the issues above.'); // Provide specific recommendations if (!this.results.localServer) { console.log('\n๐Ÿ’ก Local Server Issues:'); console.log(' - Check if port conflicts exist'); console.log(' - Verify server startup code'); } if (!this.results.tunnelCreation) { console.log('\n๐Ÿ’ก Tunnel Creation Issues:'); console.log(' - Check internet connectivity'); console.log(' - Verify tunnel service availability'); console.log(' - Consider disabling Pinggy if it consistently fails'); } if (!this.results.fileServing) { console.log('\n๐Ÿ’ก File Serving Issues:'); console.log(' - The tunnel may be serving the status page instead of files'); console.log(' - Check URL routing in handleShareRequest'); console.log(' - Verify file path normalization'); } } } } // CLI interface if (import.meta.url === `file://${process.argv[1]}`) { const filePath = process.argv[2] || './README.md'; const diagnostic = new TunnelDiagnostic(); diagnostic.runDiagnostic(filePath).catch(console.error); } export { TunnelDiagnostic };