@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
JavaScript
// 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 };